/* -*- 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 #include "guppi-gnumeric-view.h" #include "guppi-gnumeric-manager.h" #include "guppi-gnumeric-vector.h" #include "guppi-gnumeric-xml.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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; }