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