/* This is -*- C -*- */
/* $Id: guppi-object-pie.c,v 1.3 2002/01/08 06:28:53 trow Exp $ */

/*
 * guppi-object-pie.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-useful.h>
#include <guppi-seq-scalar-core.h>
#include <guppi-seq-string-core.h>
#include <guppi-color-palette.h>
#include <guppi-group-state.h>
#include <guppi-group-view.h>
#include <guppi-group-view-layout.h>
#include <guppi-canvas-group.h>
#include "guppi-object-pie.h"

static GtkObjectClass *parent_class = NULL;

enum {
  ARG_0,
  ARG_DATA_SIZE,
  ARG_DATA,
  ARG_LABELS,
  ARG_COLORS,
  ARG_COLORS_RGB,
  ARG_RADIUS_SIZE,
  ARG_RADIUS_LOCK,
  ARG_RADIUS_MAXIMIZE,
  ARG_LEGEND_FONT,

  ARG_SLICE_CALLBACK1,
  ARG_SLICE_CALLBACK1_DATA,
  ARG_SLICE_CALLBACK1_NAME,

  ARG_SLICE_CALLBACK2,
  ARG_SLICE_CALLBACK2_DATA,
  ARG_SLICE_CALLBACK2_NAME,

  ARG_SLICE_CALLBACK3,
  ARG_SLICE_CALLBACK3_DATA,
  ARG_SLICE_CALLBACK3_NAME,

  ARG_LEGEND_CALLBACK1,
  ARG_LEGEND_CALLBACK1_DATA,
  ARG_LEGEND_CALLBACK1_NAME,

  ARG_LEGEND_CALLBACK2,
  ARG_LEGEND_CALLBACK2_DATA,
  ARG_LEGEND_CALLBACK2_NAME,

  ARG_LEGEND_CALLBACK3,
  ARG_LEGEND_CALLBACK3_DATA,
  ARG_LEGEND_CALLBACK3_NAME
};

static void
guppi_object_pie_set_arg (GtkObject *obj, GtkArg *arg, guint arg_id)
{
  GuppiObjectPie *opie = GUPPI_OBJECT_PIE (obj);
  const gchar **ptr;
  gint i;

  switch (arg_id) {

  case ARG_DATA_SIZE:
    opie->data_size = GTK_VALUE_INT (*arg);
    if (opie->data_size <= 0)
      g_warning ("illegal data size: %d", opie->data_size);
    break;

  case ARG_DATA:
    if (opie->data_size <= 0)
      g_warning ("data: Illegal data size: %d", opie->data_size);
    else {
      guppi_free (opie->data);
      opie->data = guppi_new (double, opie->data_size);
      memcpy (opie->data, GTK_VALUE_POINTER (*arg),
	      opie->data_size *sizeof (double));
    }
    break;

  case ARG_LABELS:
    if (opie->data_size <= 0)
      g_warning ("labels: illegal data size: %d", opie->data_size);
    else {

      if (opie->labels) {
	for (i = 0; i < opie->data_size; ++i)
	  guppi_free (opie->labels[i]);
	guppi_free (opie->labels);
      }

      ptr = (const gchar **) GTK_VALUE_POINTER (*arg);

      opie->labels = guppi_new (gchar *, opie->data_size);
      for (i = 0; i < opie->data_size; ++i)
	opie->labels[i] = guppi_strdup (ptr[i]);
    }
    break;

  case ARG_COLORS:
    if (opie->data_size <= 0)
      g_warning ("colors: illegal data size: %d", opie->data_size);
    else {

      if (opie->colors) {
	for (i = 0; i < opie->data_size; ++i)
	  guppi_free (opie->colors[i]);
	guppi_free (opie->colors);
      }

      ptr = (const gchar **) GTK_VALUE_POINTER (*arg);

      opie->colors = guppi_new (gchar *, opie->data_size);
      for (i = 0; i < opie->data_size; ++i)
	opie->colors[i] = guppi_strdup (ptr[i]);
    }
    break;

  case ARG_COLORS_RGB:
    if (opie->data_size <= 0)
      g_warning ("colors_rgb: Illegal data size: %d", opie->data_size);
    else {
      guppi_free (opie->colors_rgb);
      opie->colors_rgb = guppi_new (guint32, opie->data_size);
      memcpy (opie->colors_rgb, GTK_VALUE_POINTER (*arg),
	      opie->data_size * sizeof (guint32));
    }
    break;

  case ARG_RADIUS_SIZE:
    opie->radius_size = GTK_VALUE_DOUBLE (*arg);
    break;

  case ARG_RADIUS_MAXIMIZE:
    opie->radius_maximize = GTK_VALUE_BOOL (*arg);
    break;

  case ARG_RADIUS_LOCK:
    opie->radius_lock = GTK_VALUE_BOOL (*arg);
    break;

  case ARG_LEGEND_FONT:
    guppi_refcounting_assign (opie->legend_font,
			      GNOME_FONT (GTK_VALUE_POINTER (*arg)));
    break;


  case ARG_SLICE_CALLBACK1:
    opie->slice_callback1 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK1_DATA:
    opie->slice_callback1_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK1_NAME:
    opie->slice_callback1_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;


  case ARG_SLICE_CALLBACK2:
    opie->slice_callback2 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK2_DATA:
    opie->slice_callback2_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK2_NAME:
    opie->slice_callback2_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;


  case ARG_SLICE_CALLBACK3:
    opie->slice_callback3 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK3_DATA:
    opie->slice_callback3_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_SLICE_CALLBACK3_NAME:
    opie->slice_callback3_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;



  case ARG_LEGEND_CALLBACK1:
    opie->legend_callback1 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK1_DATA:
    opie->legend_callback1_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK1_NAME:
    opie->legend_callback1_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;


  case ARG_LEGEND_CALLBACK2:
    opie->legend_callback2 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK2_DATA:
    opie->legend_callback2_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK2_NAME:
    opie->legend_callback2_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;


  case ARG_LEGEND_CALLBACK3:
    opie->legend_callback3 =
      (void (*)(gint, gpointer)) GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK3_DATA:
    opie->legend_callback3_data = GTK_VALUE_POINTER (*arg);
    break;

  case ARG_LEGEND_CALLBACK3_NAME:
    opie->legend_callback3_name = guppi_strdup (GTK_VALUE_POINTER (*arg));
    break;

  default:
    break;
  }
}

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

static void
guppi_object_pie_destroy (GtkObject *obj)
{
  if (parent_class->destroy)
    parent_class->destroy (obj);
}

static void
guppi_object_pie_finalize (GtkObject *obj)
{
  GuppiObjectPie *objpie = GUPPI_OBJECT_PIE (obj);
  gint i;

  guppi_free (objpie->data);

  if (objpie->labels)
    for (i = 0; i < objpie->data_size; ++i)
      guppi_free (objpie->labels[i]);
  guppi_free (objpie->labels);

  if (objpie->colors)
    for (i = 0; i < objpie->data_size; ++i)
      guppi_free (objpie->colors[i]);
  guppi_free (objpie->colors);

  guppi_free (objpie->colors_rgb);

  guppi_unref (objpie->legend_font);

  guppi_free (objpie->slice_callback1_name);
  guppi_free (objpie->slice_callback2_name);
  guppi_free (objpie->slice_callback3_name);

  guppi_free (objpie->legend_callback1_name);
  guppi_free (objpie->legend_callback2_name);
  guppi_free (objpie->legend_callback3_name);

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

}

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

static GuppiSeqScalar *
pie_build_data(GuppiObjectPie *opie) 
{
  GuppiSeqScalar *data;
  int i;

  data = GUPPI_SEQ_SCALAR (guppi_seq_scalar_core_new ());

  for(i = 0; i < opie->data_size; i++) 
    guppi_seq_scalar_append(data, opie->data[i]);

  return data;  
}

static GuppiSeqString *
pie_build_labels(GuppiObjectPie *opie) 
{
  GuppiSeqString *labels;
  int i;

  labels = GUPPI_SEQ_STRING (guppi_seq_string_core_new ());

  for(i = 0; i < opie->data_size; i++) 
    guppi_seq_string_append (labels, opie->labels[i]);

  return labels;  
}

static GuppiColorPalette *
pie_build_colors(GuppiObjectPie *opie) 
{
  GuppiColorPalette *palette;
  guint32 c;
  int i;

  palette = guppi_color_palette_new ();

  guppi_color_palette_set_custom (palette, opie->data_size, NULL);

  for(i = 0; i < opie->data_size; i++) {
    if (opie->colors_rgb) {
      c = RGB_TO_RGBA (opie->colors_rgb[i], 0xff);    
    } else {
      c = guppi_str2color_rgba (opie->colors[i]);
      if(!c)
	g_message ("Unknown color: \"%s\"", opie->colors[i]);
    }
    guppi_color_palette_set (palette, i, c);
  }
  
  return palette;
}

static void
update (GuppiObject *obj) 
{
  GuppiGroupView *grp_view;
  GuppiObjectPie *opie;
  GuppiSeqScalar *pie_data;
  GuppiSeqString *pie_labels = NULL;
  GuppiColorPalette *pie_colors = NULL;
  GuppiElementState *legend_state = NULL;
  GuppiElementState *pie_state = NULL;	
  GnomeFont *font;
  
  g_return_if_fail (obj != NULL);
  g_return_if_fail (GUPPI_IS_OBJECT_PIE (obj));
  
  opie = GUPPI_OBJECT_PIE (obj);
  grp_view = GUPPI_GROUP_VIEW (guppi_object_view (obj));
  
  if (opie->data == NULL) {
    g_warning("No pie data specified.");
    return;
  }
  
  pie_state = guppi_element_view_state(opie->pie_view);
  guppi_ref (pie_state);
  
  /* FIXME: if the object now contains data for a lengend, but that
     legend wasn't constructed in build(), we may need to construct it
     here.  Right now it dosen't do that. */

  if (opie->legend_view) {
    legend_state = guppi_element_view_state (opie->legend_view);
    guppi_ref (legend_state);
  }

  pie_data = pie_build_data(opie);

  if (opie->labels) {
    pie_labels = pie_build_labels(opie);
  }
  
  if (opie->colors_rgb || opie->colors) {
    pie_colors = pie_build_colors(opie);
  }

  guppi_element_state_set (pie_state,
			   "data", pie_data,
			   "radius_maximize", opie->radius_maximize,
			   "radius_lock", opie->radius_lock,
			   "radius", opie->radius_size,
			   "data_labels", pie_labels,
			   "data_styles", pie_colors,
			   NULL);

  guppi_element_state_changed(pie_state);
  
  if (legend_state) {
    font = opie->legend_font;

    if (font == NULL) 
      font = guppi_default_font ();

    guppi_element_state_set (legend_state,
			     "labels", pie_labels,
			     "swatch_colors", pie_colors,
			     "fallback_to_stock_colors", TRUE,
			     "label_font::adopt", font,
			     NULL);
  }

  guppi_unref (legend_state);
  guppi_unref (pie_state);
  guppi_unref (pie_data);
  guppi_unref (pie_labels);
  guppi_unref (pie_colors);  
}

static gpointer
build (GuppiObject *obj, double hsize, double vsize)
{
  GuppiObjectPie *opie;
  GuppiElementState *grp_state;
  GuppiGroupView *grp_view;

  GnomeFont *font;

  GuppiSeqScalar *pie_data;
  GuppiSeqString *pie_labels = NULL;
  GuppiColorPalette *pie_colors = NULL;

  GuppiElementState *legend_state = NULL;
  GuppiElementState *pie_state;

  GuppiElementView *legend_view = NULL;
  GuppiElementView *pie_view;


  const double outer_margin = 3.6;
  const double inner_gap = 3.6;

  g_return_val_if_fail (obj != NULL, NULL);
  g_return_val_if_fail (GUPPI_IS_OBJECT_PIE (obj), NULL);

  opie = GUPPI_OBJECT_PIE (obj);

  if (opie->data == NULL) {
    g_warning ("No pie data specified.");
    return NULL;
  }

  /* Construct the group which will contain our item. */
  grp_state = guppi_group_state_new ();
  grp_view = GUPPI_GROUP_VIEW (guppi_element_state_make_view (grp_state));
  guppi_unref0 (grp_state);

  /* Copy our data into a GuppiSeqScalar */
  pie_data = pie_build_data(opie);


  if (opie->labels) 
    pie_labels = pie_build_labels(opie);
  

  if (opie->colors_rgb || opie->colors)
    pie_colors = pie_build_colors (opie);
  


  /* We should take the font size into account here. */

  pie_state = guppi_element_state_new ("pie",
				       "data", pie_data,
				       "radius_maximize", opie->radius_maximize, 
				       "radius_lock", opie->radius_lock,
				       "radius", opie->radius_size,
				       "label_data", pie_labels,
				       "slice_colors", pie_colors,
				       NULL);

  pie_view = guppi_element_state_make_view (pie_state);


  if (pie_labels != NULL) {

    font = opie->legend_font;

    if (font == NULL)
      font = guppi_default_font ();

    legend_state = guppi_element_state_new ("legend",
					    "labels", pie_labels,
					    "swatch_colors", pie_colors,
					    "label_font", font, NULL);

    legend_view = guppi_element_state_make_view (legend_state);

  }

  /* Lay out our plot */

  guppi_group_view_layout_fill_vertically (grp_view, pie_view, outer_margin, outer_margin);
  guppi_group_view_layout_flush_left (grp_view, pie_view, outer_margin);

  if (legend_view) {

    guppi_group_view_layout_flush_right (grp_view, legend_view, outer_margin);
    guppi_group_view_layout_same_vertical_center (grp_view, legend_view, pie_view);
    guppi_group_view_layout_horizontally_adjacent (grp_view,
						   pie_view, legend_view,
						   inner_gap);

  } else {

    guppi_group_view_layout_flush_right (grp_view, pie_view, outer_margin);

  }

  /* Save our view objects */

  opie->pie_view = pie_view;
  opie->legend_view = legend_view;


  /* Clean up */
  guppi_unref (pie_data);
  guppi_unref (pie_labels);
  guppi_unref (pie_colors);
  guppi_unref (pie_state);
  guppi_unref (legend_state);

  return GUPPI_ELEMENT_VIEW (grp_view);
}

static void
clicked_slice_cb (GuppiCanvasItem *item, gint slice,
		  guint button, guint state, GuppiObjectPie *opie)
{
  if (button == 1 && opie->slice_callback1) {

    opie->slice_callback1 (slice, opie->slice_callback1_data);

  } else if (button == 2 && opie->slice_callback2) {

    opie->slice_callback2 (slice, opie->slice_callback2_data);

  } else if (button == 3 && opie->slice_callback3) {

    opie->slice_callback3 (slice, opie->slice_callback3_data);

  }
}

static void
clicked_box_cb (GuppiCanvasItem *item, gint box,
		guint button, guint state, GuppiObjectPie *opie)
{
  if (button == 1 && opie->legend_callback1) {

    opie->legend_callback1 (box, opie->legend_callback1_data);

  } else if (button == 2 && opie->legend_callback2) {

    opie->legend_callback2 (box, opie->legend_callback2_data);

  } else if (button == 3 && opie->legend_callback3) {

    opie->legend_callback3 (box, opie->legend_callback3_data);

  }
}

static void
item_init (GuppiObject *obj, GnomeCanvasItem *item)
{
  GuppiObjectPie *pieobj = GUPPI_OBJECT_PIE (obj);
  GuppiCanvasGroup *grp_item = GUPPI_CANVAS_GROUP (item);
  GuppiCanvasItem *pie_item;
  GuppiCanvasItem *legend_item;

  pie_item = guppi_canvas_group_find_by_view (grp_item, pieobj->pie_view);
  legend_item =
    guppi_canvas_group_find_by_view (grp_item, pieobj->legend_view);

  if (pie_item) {
    gtk_signal_connect (GTK_OBJECT (pie_item),
			"clicked_slice",
			GTK_SIGNAL_FUNC (clicked_slice_cb), pieobj);
  }


  if (legend_item) {
    gtk_signal_connect (GTK_OBJECT (legend_item),
			"clicked_box",
			GTK_SIGNAL_FUNC (clicked_box_cb), pieobj);
  }
}

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

#define add_arg(str, t, symb) \
gtk_object_add_arg_type("GuppiObjectPie::" str, t, GTK_ARG_WRITABLE, symb)

static void
guppi_object_pie_class_init (GuppiObjectPieClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiObjectClass *gobj_class = GUPPI_OBJECT_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_OBJECT);

  object_class->set_arg = guppi_object_pie_set_arg;
  object_class->destroy = guppi_object_pie_destroy;
  object_class->finalize = guppi_object_pie_finalize;

  gobj_class->build = build;
  gobj_class->item_init = item_init;
  gobj_class->update = update;

  add_arg ("data_size", GTK_TYPE_INT, ARG_DATA_SIZE);
  add_arg ("data", GTK_TYPE_POINTER, ARG_DATA);
  add_arg ("labels", GTK_TYPE_POINTER, ARG_LABELS);
  add_arg ("colors", GTK_TYPE_POINTER, ARG_COLORS);
  add_arg ("colors_rgb", GTK_TYPE_POINTER, ARG_COLORS_RGB);
  add_arg ("radius_size", GTK_TYPE_DOUBLE, ARG_RADIUS_SIZE);
  add_arg ("radius_maximize", GTK_TYPE_BOOL, ARG_RADIUS_MAXIMIZE);
  add_arg ("radius_lock", GTK_TYPE_BOOL, ARG_RADIUS_LOCK);

  add_arg ("legend_font", GTK_TYPE_POINTER, ARG_LEGEND_FONT);

  add_arg ("slice_callback1", GTK_TYPE_POINTER, ARG_SLICE_CALLBACK1);
  add_arg ("slice_callback1_data", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK1_DATA);
  add_arg ("slice_callback1_name", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK1_NAME);

  add_arg ("slice_callback2", GTK_TYPE_POINTER, ARG_SLICE_CALLBACK2);
  add_arg ("slice_callback2_data", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK2_DATA);
  add_arg ("slice_callback2_name", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK2_NAME);

  add_arg ("slice_callback3", GTK_TYPE_POINTER, ARG_SLICE_CALLBACK3);
  add_arg ("slice_callback3_data", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK3_DATA);
  add_arg ("slice_callback3_name", GTK_TYPE_POINTER,
	   ARG_SLICE_CALLBACK3_NAME);

  add_arg ("legend_callback1", GTK_TYPE_POINTER, ARG_LEGEND_CALLBACK1);
  add_arg ("legend_callback1_data", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK1_DATA);
  add_arg ("legend_callback1_name", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK1_NAME);

  add_arg ("legend_callback2", GTK_TYPE_POINTER, ARG_LEGEND_CALLBACK2);
  add_arg ("legend_callback2_data", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK2_DATA);
  add_arg ("legend_callback2_name", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK2_NAME);

  add_arg ("legend_callback3", GTK_TYPE_POINTER, ARG_LEGEND_CALLBACK3);
  add_arg ("legend_callback3_data", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK3_DATA);
  add_arg ("legend_callback3_name", GTK_TYPE_POINTER,
	   ARG_LEGEND_CALLBACK3_NAME);
}

static void
guppi_object_pie_init (GuppiObjectPie *obj)
{
  obj->data_size = 0;
  obj->radius_maximize = TRUE;
}

GtkType guppi_object_pie_get_type (void)
{
  static GtkType guppi_object_pie_type = 0;
  if (!guppi_object_pie_type) {
    static const GtkTypeInfo guppi_object_pie_info = {
      "GuppiObjectPie",
      sizeof (GuppiObjectPie),
      sizeof (GuppiObjectPieClass),
      (GtkClassInitFunc) guppi_object_pie_class_init,
      (GtkObjectInitFunc) guppi_object_pie_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_object_pie_type =
      gtk_type_unique (GUPPI_TYPE_OBJECT, &guppi_object_pie_info);
  }
  return guppi_object_pie_type;
}

/* $Id: guppi-object-pie.c,v 1.3 2002/01/08 06:28:53 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1