/* 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 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-pie-state.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 $ */