/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* vim: set sw=8: */
/*
* guppi-gnumeric-graph.c: a container class to manage extra data elements and
* views of an xml spec
*
* Copyright (C) 2001 Ximian, Inc
*
* Developed by Jody Goldberg (jgoldberg@home.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-gnumeric-view.h"
#include "guppi-gnumeric-manager.h"
#include "guppi-gnumeric-vector.h"
#include "guppi-gnumeric-xml.h"
#include <guppi-memory.h>
#include <guppi-seq-string.h>
#include <guppi-seq-string-core.h>
#include <guppi-seq-scalar.h>
#include <guppi-seq-scalar-core.h>
#include <guppi-root-group-item.h>
#include <guppi-root-group-state.h>
#include <guppi-group-view-layout.h>
#include <guppi-metrics.h>
#include <guppi-enums.h>
#include <guppi-defaults.h>
#include <guppi-rgb.h>
#include <gal/util/e-xml-utils.h>
/* Ideally this would be 0 for an
* un-anti-aliased un scale hair width
* line
*/
#define HAIR_WIDTH .5
#define GAP 3.6 /* 1/20 inch */
GupGnmPlotDescriptor const *
gup_gnm_plot_get_descriptor (xmlNode *plot)
{
extern GupGnmPlotDescriptor gup_gnm_scatter_plot_type;
extern GupGnmPlotDescriptor gup_gnm_pie_plot_type;
extern GupGnmPlotDescriptor gup_gnm_line_plot_type;
extern GupGnmPlotDescriptor gup_gnm_bar_plot_type;
static GupGnmPlotDescriptor const * const plot_types[] = {
&gup_gnm_scatter_plot_type,
&gup_gnm_pie_plot_type,
&gup_gnm_line_plot_type,
&gup_gnm_bar_plot_type,
NULL
};
xmlNode *type_node = e_xml_get_child_by_name (plot, "Type");
xmlChar *type;
int i;
g_return_val_if_fail (type_node != NULL, NULL);
type = xmlGetProp (type_node, "name");
g_return_val_if_fail (type != NULL, NULL);
for (i = 0 ; plot_types[i] != NULL; i++)
if (!strcmp (plot_types[i]->name, type)) {
xmlFree (type);
return plot_types[i];
}
xmlFree (type);
return NULL;
}
static void
gup_gnm_graph_clear_names (GupGnmGraph *graph)
{
GupGnmManager *manager = graph->manager;
if (manager->vectors != NULL) {
int i;
i = manager->vectors->len;
while (i-- > 0) {
GupGnmVector *vector = g_ptr_array_index (manager->vectors, i);
if (vector != NULL)
gup_gnm_vector_clear_names (vector, graph, -1);
}
}
}
/**
* gup_gnm_graph_add_wrapper :
* @graph :
* @wrapper :
*
* Adds @wrapper to the collection of sequences whose lifecyle is controllled
* by the @graph.
*/
void
gup_gnm_graph_add_wrapper (GupGnmGraph *graph, GuppiData *wrapper)
{
if (graph->wrapper_sequences == NULL)
graph->wrapper_sequences = g_ptr_array_new ();
g_ptr_array_add (graph->wrapper_sequences, wrapper);
}
static void
gup_gnm_graph_clear_wrappers (GupGnmGraph *graph)
{
if (graph->wrapper_sequences != NULL) {
int i;
i = graph->wrapper_sequences->len;
while (i-- > 0) {
GupGnmVector *vector = g_ptr_array_index (graph->wrapper_sequences, i);
if (vector != NULL)
guppi_unref (GTK_OBJECT (vector));
}
g_ptr_array_free (graph->wrapper_sequences, TRUE);
graph->wrapper_sequences = NULL;
}
}
static char *
gup_gnm_series_calc_name (xmlNode *series, int seriesIndex, GupGnmGraph *graph)
{
char *name = NULL;
GupGnmVector *vector = gup_gnm_series_dim_get_vector (series, "labels", graph);
if (vector != NULL) {
GuppiSeqString *seqs = gup_gnm_vector_get_string (vector);
if (seqs != NULL) {
gup_gnm_vector_mark_as_name (vector, graph, seriesIndex);
name = guppi_strdup (guppi_seq_string_get (seqs, 0));
}
} else {
xmlChar *tmp = xmlGetProp (series, "name");
if (tmp != NULL) {
name = guppi_strdup (tmp);
xmlFree (tmp);
}
}
if (name == NULL)
name = guppi_strdup_printf ("Series%d", seriesIndex+1);
xmlSetProp (series, "name", name);
return name;
}
typedef struct {
int seriesID;
char const *dim_name;
int vectorID;
gboolean setting_shared;
} SeriesDimClosure;
static gboolean
cb_series_set_dim (GupGnmGraph *graph,
xmlNode *plot, xmlNode *series,
gpointer user)
{
SeriesDimClosure *info = user;
xmlNode *dim;
/* FIXME : get an 'ID' rather than using index */
int ID = gup_gnm_series_get_id (series);
gboolean is_label;
/* Use negative seriesIDs as a wildcard */
if (info->seriesID >= 0 && ID != info->seriesID)
return FALSE;
is_label = (0 == strcmp (info->dim_name, "labels"));
dim = gup_gnm_series_get_dimension (series,
info->dim_name);
if (dim != NULL) {
int id = e_xml_get_integer_prop_by_name_with_default (dim, "ID", -1);
/* clear out the old label if it exists */
if (is_label && id >= 0)
gup_gnm_vector_clear_names (
gup_gnm_manager_get_vector (graph->manager, id),
graph, info->seriesID);
if (info->vectorID < 0) {
xmlUnlinkNode (dim);
xmlFreeNode (dim);
} else
e_xml_set_integer_prop_by_name (dim, "ID", info->vectorID);
} else if (info->vectorID >= 0)
gup_gnm_series_add_dimension (series,
info->dim_name, info->vectorID);
/* else it was already empty */
/* store the new name */
if (is_label)
guppi_seq_string_set_nc (
graph->series.names,
ID, gup_gnm_series_calc_name (series, ID, graph));
/* If this is a shared dim change the other members of this plot */
if (info->seriesID >= 0) {
int i;
xmlNode *ptr;
GupGnmPlotDescriptor const *descriptor =
gup_gnm_plot_get_descriptor (plot);
g_return_val_if_fail (descriptor != NULL, TRUE);
for (i = 0; descriptor->spec [i].display_name != NULL ; i++) {
if (strcmp (info->dim_name, descriptor->spec [i].dim_name))
continue;
if (!descriptor->spec[i].shared)
break;
info->seriesID = -1;
for (ptr = series->parent->xmlChildrenNode ; ptr ; ptr = ptr->next) {
if (ptr == series ||
strcmp (series->name, "Series"))
continue;
cb_series_set_dim (graph, plot, ptr, user);
}
break;
}
}
return TRUE;
}
/**
* gup_gnm_graph_set_series_dim :
*
* Find the series with the supplied ID and change the specified dimension.
* Handle special case of shared dimensions, and names.
*
* returns TRUE if things succeeded.
*/
gboolean
gup_gnm_graph_set_series_dim (GupGnmGraph *graph,
int seriesID, char const *dim_name,
int vectorID)
{
SeriesDimClosure closure;
g_return_val_if_fail (graph != NULL, FALSE);
g_return_val_if_fail (dim_name != NULL, FALSE);
g_return_val_if_fail (seriesID >= 0, FALSE);
closure.seriesID = seriesID;
closure.dim_name = dim_name;
closure.vectorID = vectorID;
return gup_gnm_graph_foreach_series (graph, cb_series_set_dim, &closure);
}
void
gup_gnm_graph_arrange_data (GupGnmGraph const *graph,
xmlNode *plot, xmlNode *data,
char const **extras, int extra_count)
{
GupGnmManager *manager = graph->manager;
int j, i = 0, cat = -1, count = 0;
int max = manager->arrangement_len - extra_count;
if (manager->arrangement_len > 1) {
cat = manager->data_ids[0];
i = 1;
}
for (;i < max ; i++) {
xmlNode *series;
series = xmlNewChild (data, data->ns, "Series", NULL);
e_xml_set_integer_prop_by_name (series, "index", count++);
if (manager->header_ids[i] >= 0)
gup_gnm_series_add_dimension (series, "labels",
manager->header_ids[i]);
gup_gnm_series_add_dimension (series, "values", manager->data_ids[i]);
if (cat >= 0)
gup_gnm_series_add_dimension (series, "categories", cat);
for (j = 0 ; j < extra_count ; j++)
gup_gnm_series_add_dimension (series, extras[j],
manager->data_ids[++i]);
}
}
static GuppiRootGroupView *
gup_gnm_graph_make_plot (GupGnmGraph *graph)
{
GupGnmPlotDescriptor const *descriptor = NULL;
xmlNode *node, *plot;
guppi_compass_t legend_location;
gboolean singleton = FALSE;
/* Models */
GuppiElementState *root_state;
GuppiElementState *plot_state, *legend_state;
GuppiElementState *compassbox_state;
GSList *plot_states = NULL;
/* View Objects */
GuppiGroupView *root_view;
GuppiElementView *plot_view, *legend_view = NULL;
GuppiElementView *compassbox_view;
GuppiElementView *frame_view, *x_axis_view, *y_axis_view;
GuppiGroupView *group_view;
g_return_val_if_fail (graph->spec != NULL, NULL);
plot = e_xml_get_child_by_name (graph->spec->xmlRootNode, "Plots");
g_return_val_if_fail (plot != NULL, NULL);
gup_gnm_graph_clear_wrappers (graph);
plot_states = NULL;
for (plot = plot->xmlChildrenNode ; plot ; plot = plot->next) {
if (strcmp (plot->name, "Plot"))
continue;
if (NULL != (descriptor = gup_gnm_plot_get_descriptor (plot))) {
plot_states = g_slist_concat (plot_states,
descriptor->plot (graph, plot));
singleton = descriptor->singleton;
}
}
plot_state = NULL;
if (plot_states != NULL) {
GSList *tmp = plot_states;
plot_states = plot_states->next;
plot_state = tmp->data;
} else {
plot_state = guppi_element_state_new("text",
"font", guppi_default_font_large (),
"text", "Unimplemented",
NULL);
singleton = FALSE;
}
/* Legend */
legend_state = NULL;
legend_location = GUPPI_COMPASS_INVALID;
node = e_xml_get_child_by_name (graph->spec->xmlRootNode, "Legend");
if (node != NULL)
node = e_xml_get_child_by_name (node, "Position");
if (node != NULL) {
xmlChar *tmp = xmlNodeGetContent (node);
if (tmp != NULL) {
legend_location = guppi_str2compass (tmp);
xmlFree (tmp);
}
}
legend_state = guppi_element_state_new("legend",
"swatch_width", 6.,
"swatch_height", 6.,
"edge_thickness", HAIR_WIDTH,
"edge_margin", guppi_in2pt(1/16.0),
NULL);
legend_view = guppi_element_state_make_view (legend_state);
guppi_unref (legend_state);
/* guppi_element_state_changed_delayed (plot_state); */
if (singleton) {
/* This is an evil, pie-specific hack */
if (!strcmp (descriptor->name, "Pie")) {
GuppiData *d = NULL;
GuppiColorPalette *palette = NULL;
guppi_element_state_get (plot_state,
"label_data", &d,
"slice_colors", &palette,
NULL);
guppi_element_state_set (legend_state,
"labels", d,
"swatch_colors", palette,
NULL);
}
} else if (graph->series.names != NULL) {
guppi_element_state_set (legend_state,
"labels", graph->series.names,
"swatch_colors", graph->series.colours,
NULL);
}
/* Frame (i.e. Grid Lines) */
if (singleton) {
frame_view = NULL;
} else {
GuppiElementState *frame_state = guppi_element_state_new ("frame",
"minor_rule_thickness", HAIR_WIDTH,
"major_rule_thickness", HAIR_WIDTH,
"minor_rule_color", RGBA_BLACK,
"major_rule_color", RGBA_BLACK,
"frame_color", RGBA_BLACK,
"frame_thickness", .25,
NULL);
frame_view = guppi_element_view_new (frame_state, NULL);
guppi_unref (frame_state);
}
/* Compass Box */
compassbox_state = guppi_element_state_new ("compass-box",
"position", legend_location,
NULL);
compassbox_view = guppi_element_view_new (compassbox_state, NULL);
guppi_unref (compassbox_state);
/* Axes */
if (singleton) {
x_axis_view = y_axis_view = NULL;
} else {
GuppiElementState *x_axis_state = guppi_element_state_new ("axis",
"minor_tick_thickness", .25,
"major_tick_thickness", .5,
"position", GUPPI_SOUTH,
NULL);
GuppiElementState *y_axis_state = guppi_element_state_new ("axis",
"minor_tick_thickness", .25,
"major_tick_thickness", .5,
"position", GUPPI_WEST,
NULL);
x_axis_view = guppi_element_view_new (x_axis_state, NULL);
y_axis_view = guppi_element_view_new (y_axis_state, NULL);
guppi_unref (x_axis_state);
guppi_unref (y_axis_state);
}
root_state = guppi_root_group_state_new ();
root_view = GUPPI_GROUP_VIEW (guppi_element_state_make_view (root_state));
guppi_unref (root_state);
plot_view = guppi_element_state_make_view (plot_state);
if (descriptor && descriptor->process_view)
descriptor->process_view (plot_view);
/* This view contains our plots and (if they exist) our axes */
group_view = guppi_group_view_new ();
if (frame_view) {
static guint32 const grey = RGBA_TO_UINT(0xd7,0xd7,0xd7,0xff);
GuppiElementState *back_state = guppi_element_state_new ("background",
#ifdef EYE_CANDY
"color", 0xe0e0ffff,
"color_final", 0xffffffff,
"gradient_start", GUPPI_SOUTH,
#else
"color", grey,
"color_final", grey,
#endif
NULL);
GuppiElementView *background_view =
guppi_element_view_new (back_state, NULL);
guppi_unref (back_state);
guppi_group_view_layout_same_place (group_view, background_view, frame_view);
guppi_group_view_layout_same_place (group_view, frame_view, plot_view);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_X_AXIS, frame_view, GUPPI_X_AXIS);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_Y_AXIS, frame_view, GUPPI_Y_AXIS);
}
if (y_axis_view) {
guppi_group_view_layout_flush_top (group_view, y_axis_view, GAP);
guppi_group_view_layout_flush_left (group_view, y_axis_view, GAP);
guppi_group_view_layout_horizontally_aligned (group_view, y_axis_view, plot_view, 0.);
guppi_group_view_layout_flush_right (group_view, plot_view, GAP);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_Y_AXIS, y_axis_view, GUPPI_Y_AXIS);
} else {
guppi_group_view_layout_fill_horizontally (group_view, plot_view, GAP, GAP);
}
if (x_axis_view) {
guppi_group_view_layout_flush_bottom (group_view, x_axis_view, GAP);
guppi_group_view_layout_flush_right (group_view, x_axis_view, GAP);
guppi_group_view_layout_vertically_aligned (group_view, plot_view, x_axis_view, 0.);
guppi_group_view_layout_flush_top (group_view, plot_view, GAP);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_X_AXIS, x_axis_view, GUPPI_X_AXIS);
} else {
guppi_group_view_layout_fill_vertically (group_view, plot_view, GAP, GAP);
}
if (plot_states != NULL) {
GSList *ptr = plot_states;
for (; ptr != NULL; ptr = ptr->next) {
GuppiElementView *p2_view = guppi_element_state_make_view (ptr->data);
guppi_group_view_layout_same_place (group_view, plot_view, p2_view);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_X_AXIS, p2_view, GUPPI_X_AXIS);
guppi_element_view_connect_axis_markers (plot_view, GUPPI_Y_AXIS, p2_view, GUPPI_Y_AXIS);
guppi_unref (ptr->data);
}
g_slist_free (plot_states);
}
guppi_group_view_add ((GuppiGroupView *) compassbox_view, legend_view);
guppi_group_view_add ((GuppiGroupView *) compassbox_view, (GuppiElementView *) group_view);
guppi_group_view_layout_fill (root_view, compassbox_view, GAP, GAP, GAP, GAP);
guppi_element_view_set_preferred_view_all (plot_view);
guppi_root_group_view_set_size (GUPPI_ROOT_GROUP_VIEW (root_view),
6*72, 6*72);
#if 0
guppi_root_group_item_set_resize_semantics (GUPPI_ROOT_GROUP_ITEM (view->item),
ROOT_GROUP_RESIZE_FILL_SPACE);
#endif
guppi_unref (plot_state);
return GUPPI_ROOT_GROUP_VIEW (root_view);
}
/**
* gup_gnm_graph_get_view :
* @graph :
*
* Get a possibly cached view.
*/
GuppiRootGroupView *
gup_gnm_graph_get_view (GupGnmGraph *graph)
{
if (graph->plot == NULL)
graph->plot = gup_gnm_graph_make_plot (graph);
return graph->plot;
}
/**
* gup_gnm_graph_regenerate_plots :
* @graph :
*/
void
gup_gnm_graph_regenerate_plots (GupGnmGraph *graph)
{
GList *view;
if (graph->plot != NULL)
guppi_unref (GTK_OBJECT (graph->plot));
graph->plot = gup_gnm_graph_make_plot (graph);
for (view = graph->views ; view != NULL ; view = view->next)
gup_gnm_view_regenerate (view->data);
}
guint32
gup_gnm_graph_get_series_color (GupGnmGraph const *graph, xmlNode *series)
{
int i = e_xml_get_integer_prop_by_name_with_default (series,
"index", -1);
g_return_val_if_fail (i >= 0, 0);
return guppi_color_palette_get (graph->series.colours, i);
}
GuppiMarker
gup_gnm_graph_get_series_marker (GupGnmGraph const *graph, xmlNode *series)
{
int i = e_xml_get_integer_prop_by_name_with_default (series,
"index", -1);
g_return_val_if_fail (i >= 0, 0);
return g_array_index (graph->series.markers, GuppiMarker, i);
}
void
gup_gnm_graph_construct (GupGnmGraph *graph, GupGnmManager *manager)
{
graph->manager = manager;
graph->plot = NULL;
graph->views = NULL;
graph->spec = NULL;
graph->wrapper_sequences = NULL;
graph->series.names = guppi_seq_string_core_new ();
graph->series.colours = guppi_color_palette_new ();
graph->series.markers = g_array_new (FALSE, TRUE, sizeof (GuppiMarker));
}
void
gup_gnm_graph_release (GupGnmGraph *graph)
{
if (graph->manager != NULL) {
if (graph->series.names != NULL)
guppi_unref0 (graph->series.names);
if (graph->series.colours != NULL)
guppi_unref0 (graph->series.colours);
if (graph->series.markers != NULL)
g_array_free (graph->series.markers, TRUE);
if (graph->spec != NULL) {
xmlFreeDoc (graph->spec);
graph->spec = NULL;
}
}
}
typedef struct {
char const *name;
int seriesID;
} SeriesNameClosure;
static gboolean
cb_store_series_name (GupGnmGraph *graph,
xmlNode *plot, xmlNode *series,
gpointer user)
{
SeriesNameClosure const *info = user;
int ID = gup_gnm_series_get_id (series);
if (ID != info->seriesID)
return FALSE;
xmlSetProp (series, "name", info->name);
guppi_seq_string_set (graph->series.names,
info->seriesID, info->name);
return TRUE;
}
/**
* gup_gnm_graph_store_series_name :
* @graph :
* @seriesID :
* @name :
*
* Store a copy of the supplied string in the series_name list,
* and update the xml.
*/
void
gup_gnm_graph_store_series_name (GupGnmGraph *graph,
int seriesID, char const *name)
{
SeriesNameClosure closure;
g_return_if_fail (graph->series.names != NULL);
closure.name = name;
closure.seriesID = seriesID;
gup_gnm_graph_foreach_series (graph, cb_store_series_name, &closure);
}
static int
ensure_index (GPtrArray *elements, xmlNode *element)
{
int tmp = e_xml_get_integer_prop_by_name_with_default (element,
"index", -1);
if (tmp < 0) {
tmp = elements->len;
e_xml_set_integer_prop_by_name (element, "index", tmp);
}
if (tmp >= elements->len)
g_ptr_array_set_size (elements, tmp+1);
g_ptr_array_index (elements, tmp) = element;
return tmp;
}
static GuppiMarker
stock_marker_alien (int indx)
{
static GuppiMarker const markers[] = {
GUPPI_MARKER_DIAMOND,
GUPPI_MARKER_SQUARE,
GUPPI_MARKER_TRIANGLE,
GUPPI_MARKER_X,
GUPPI_MARKER_AST,
GUPPI_MARKER_CIRCLE,
GUPPI_MARKER_CROSS,
GUPPI_MARKER_HALF_BAR,
GUPPI_MARKER_BAR,
};
static int const marker_count = sizeof(markers)/sizeof(GuppiMarker);
return markers [indx % marker_count];
}
void
gup_gnm_graph_markup_spec (GupGnmGraph *graph)
{
GupGnmPlotDescriptor const *descriptor;
xmlNode *plot, *series, *layout;
GPtrArray *all_plots, *all_series, *all_names;
GuppiSeq *seq;
int i;
g_return_if_fail (graph->spec != NULL);
all_plots = g_ptr_array_new ();
all_series = g_ptr_array_new ();
all_names = g_ptr_array_new ();
/* 0) Clear the names */
seq = GUPPI_SEQ (graph->series.names);
if (guppi_seq_nonempty (seq)) {
guppi_seq_delete_range (seq,
guppi_seq_min_index (seq),
guppi_seq_max_index (seq));
gup_gnm_graph_clear_names (graph);
}
/* 0.1) reset the colours to stock */
guppi_color_palette_set_alien_stock (graph->series.colours);
#if 0
guppi_color_palette_set_stock (graph->series.colours);
#endif
/* 0.2) clear out the markers too */
g_array_set_size (graph->series.markers, 0);
plot = e_xml_get_child_by_name (graph->spec->xmlRootNode, "Plots");
g_return_if_fail (plot != NULL);
for (plot = plot->xmlChildrenNode ; plot ; plot = plot->next) {
if (strcmp (plot->name, "Plot"))
continue;
/* 1) Make sure all the plots have index numbers. */
ensure_index (all_plots, plot);
/* 2) clean out old descriptors */
layout = e_xml_get_child_by_name (plot, "DataLayout");
if (layout != NULL) {
xmlUnlinkNode (layout);
xmlFreeNode (layout);
}
/* 3) Add shiny new descriptors */
if (NULL != (descriptor = gup_gnm_plot_get_descriptor (plot))) {
int i;
xmlNode *dim, *layout = xmlNewChild (plot, plot->ns,
"DataLayout", NULL);
dim = xmlNewChild (layout, layout->ns, "Dimension",
_("Name"));
xmlSetProp (dim, "dim_name", "labels");
for (i = 0; descriptor->spec [i].display_name != NULL ; i++) {
dim = xmlNewChild (layout, layout->ns, "Dimension",
_(descriptor->spec[i].display_name));
xmlSetProp (dim, "dim_name",
descriptor->spec[i].dim_name);
if (!descriptor->spec[i].optional)
e_xml_set_bool_prop_by_name (dim, "required", TRUE);
if (descriptor->spec[i].shared)
e_xml_set_bool_prop_by_name (dim, "shared", TRUE);
}
}
series = e_xml_get_child_by_name (plot, "Data");
if (series == NULL)
continue;
for (series = series->xmlChildrenNode ; series ; series = series->next) {
int indx;
char *name = NULL;
xmlNode *format;
if (strcmp (series->name, "Series"))
continue;
/* 4) Make sure all the series have index numbers. */
indx = ensure_index (all_series, series);
/* 5) handle series names */
name = gup_gnm_series_calc_name (series, indx, graph);
if (indx >= all_names->len) {
g_ptr_array_set_size (all_names, indx+1);
g_array_set_size (graph->series.markers, indx+1);
}
g_ptr_array_index (all_names, indx) = name;
/* 6) Handle series formating */
format = e_xml_get_child_by_name (series, "Format");
guppi_color_palette_set (graph->series.colours, indx,
gup_gnm_attr_get_color (format, "AreaColor",
guppi_color_palette_get (graph->series.colours, indx)));
g_array_index (graph->series.markers, GuppiMarker, indx) =
gup_gnm_attr_get_marker (format, "MarkerShape",
stock_marker_alien (indx));
}
}
for (i = 0; i < all_names->len ; i++)
guppi_seq_string_append_nc (graph->series.names,
g_ptr_array_index (all_names, i));
g_ptr_array_free (all_names, TRUE);
g_ptr_array_free (all_series, TRUE);
g_ptr_array_free (all_plots, TRUE);
gup_gnm_graph_regenerate_plots (graph);
}
/**
* gup_gnm_graph_generate_series :
*
* FIXME think about how to handle an existing set of plots.
* For now just use the first
*/
void
gup_gnm_graph_generate_series (GupGnmGraph *graph)
{
GupGnmManager *manager = graph->manager;
GupGnmPlotDescriptor const *descriptor;
xmlNode *plot, *data;
/* if no type has been selected yet, just store the data,
* we'll generate later.
*/
if (graph->spec == NULL)
return;
plot = e_xml_get_child_by_name (graph->spec->xmlRootNode, "Plots");
g_return_if_fail (plot != NULL);
plot = e_xml_get_child_by_name (plot, "Plot");
data = e_xml_get_child_by_name (plot, "Data");
if (data != NULL) {
xmlUnlinkNode (data);
xmlFreeNode (data);
}
gup_gnm_graph_clear_names (graph);
data = xmlNewChild (plot, plot->ns, "Data", NULL);
descriptor = gup_gnm_plot_get_descriptor (plot);
/* There needs to be data and a known descriptor type */
if (manager->arrangement_len >= 1 && descriptor != NULL) {
g_return_if_fail (manager->data_ids != NULL);
g_return_if_fail (manager->header_ids != NULL);
g_return_if_fail (descriptor->arrange_data != NULL);
descriptor->arrange_data (graph, plot, data);
}
gup_gnm_graph_markup_spec (graph);
}
void
gup_gnm_graph_set_spec (GupGnmGraph *graph, xmlDoc *doc, gboolean copy)
{
if (graph->spec != NULL) {
g_return_if_fail (doc != graph->spec && doc != NULL);
xmlFreeDoc (graph->spec);
}
if (doc == NULL) {
graph->spec = NULL;
return;
}
graph->spec = copy ? xmlCopyDoc (doc, TRUE) : doc;
ggd (1, {
puts ("gup_gnm_graph_set_spec :");
xmlDocDump (stdout, graph->spec);
});
gup_gnm_graph_markup_spec (graph);
}
void
gup_gnm_graph_set_plottype (GupGnmGraph *graph, xmlNode *plotinfo)
{
xmlNs *ns;
xmlNode *plot;
g_return_if_fail (plotinfo != NULL);
if (graph->spec == NULL) {
xmlNode *legend;
graph->spec = xmlNewDoc ("1.0");
graph->spec->xmlRootNode =
xmlNewDocNode (graph->spec, NULL, "Graph", NULL);
ns = xmlNewNs (graph->spec->xmlRootNode,
"http://www.gnumeric.org/graph_v1", "graph");
xmlSetNs (graph->spec->xmlRootNode, ns);
legend = xmlNewChild (graph->spec->xmlRootNode, ns,
"Legend", NULL);
xmlNewChild (legend, ns, "Position", "east");
} else {
xmlNode *p = e_xml_get_child_by_name (graph->spec->xmlRootNode,
"Plots");
if (p != NULL) {
xmlUnlinkNode (p);
xmlFreeNode (p);
}
ns = graph->spec->xmlRootNode->ns;
}
plot = xmlNewChild (
xmlNewChild (graph->spec->xmlRootNode, ns, "Plots", NULL),
ns, "Plot", NULL);
xmlAddChild (plot,
xmlCopyNode (e_xml_get_child_by_name (plotinfo, "Type"), TRUE));
gup_gnm_graph_generate_series (graph);
xmlDocDump (stdout, graph->spec);
}
GuppiSeqScalar *
gup_gnm_graph_stock_index (GupGnmGraph *graph, int const n)
{
int i;
GuppiSeqScalar *indicies = GUPPI_SEQ_SCALAR (guppi_seq_scalar_core_new ());
for (i = 0; i < n ; ++i)
guppi_seq_scalar_append (indicies, i);
gup_gnm_graph_add_wrapper (graph, GUPPI_DATA (indicies));
return indicies;
}
GuppiSeqString *
gup_gnm_graph_stock_labels (GupGnmGraph *graph, int const n)
{
int i;
GuppiSeqString *labels = GUPPI_SEQ_STRING (guppi_seq_string_core_new ());
for (i = 0; i < n ; ++i)
guppi_seq_string_append_nc (labels,
guppi_strdup_printf ("%d", i));
gup_gnm_graph_add_wrapper (graph, GUPPI_DATA (labels));
return labels;
}
syntax highlighted by Code2HTML, v. 0.9.1