1 /*
2 * go-graph-data.c :
3 *
4 * Copyright (C) 2003-2004 Jody Goldberg (jody@gnome.org)
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) version 3.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <goffice/goffice-config.h>
23 #include <goffice/goffice.h>
24
25 #include <gsf/gsf-impl-utils.h>
26 #include <glib/gi18n-lib.h>
27
28 #include <string.h>
29
30 /**
31 * SECTION: gog-series
32 * @short_description: A single data series.
33 *
34 * A #GogSeries represents a data series that can be added to a #GogPlot.
35 */
36
37 /**
38 * GogSeriesFillType:
39 * @GOG_SERIES_FILL_TYPE_Y_ORIGIN: from origin ox the X-axis.
40 * @GOG_SERIES_FILL_TYPE_X_ORIGIN: from the origin of the Y-axis
41 * @GOG_SERIES_FILL_TYPE_BOTTOM: from the chart bottom.
42 * @GOG_SERIES_FILL_TYPE_LEFT: from the chart left side.
43 * @GOG_SERIES_FILL_TYPE_TOP: from the chart top.
44 * @GOG_SERIES_FILL_TYPE_RIGHT:, from the chart right side.
45 * @GOG_SERIES_FILL_TYPE_ORIGIN: from the origin (for radar and polar plots).
46 * @GOG_SERIES_FILL_TYPE_CENTER: from the center (for radar and polar plots).
47 * @GOG_SERIES_FILL_TYPE_EDGE: from the edge (for radar and polar plots).
48 * @GOG_SERIES_FILL_TYPE_SELF: close the series curve and fills it.
49 * @GOG_SERIES_FILL_TYPE_NEXT: from next series.
50 * @GOG_SERIES_FILL_TYPE_X_AXIS_MIN: from X-axis minimum.
51 * @GOG_SERIES_FILL_TYPE_X_AXIS_MAX: from X-axis maximum.
52 * @GOG_SERIES_FILL_TYPE_Y_AXIS_MIN: from Y-axis minimum.
53 * @GOG_SERIES_FILL_TYPE_Y_AXIS_MAX: from Y-axis maximum.
54 * @GOG_SERIES_FILL_TYPE_INVALID: invalid, should not happen.
55 **/
56
57 /**
58 * GogSeriesClass:
59 * @base: base class
60 * @has_interpolation: supports interpolation.
61 * @has_fill_type: %TRUE if filling is supported.
62 * @valid_fill_type_list: list of supported #GogSeriesFillType values.
63 * @series_element_type: #GType for the series element if supported.
64 * @dim_changed: called when data changed for the series.
65 * @get_xy_data: get X and Y data.
66 * @get_interpolation_params: get interpolation parameters if any, only applies
67 * to constrained cubic spline interpolation.
68 **/
69
70 /**
71 * GogSeriesElementClass:
72 * @base: base class
73 * @gse_populate_editor: populates editor.
74 **/
75
76 /**
77 * GogSeriesPriority:
78 * @GOG_SERIES_REQUIRED: it must be there.
79 * @GOG_SERIES_SUGGESTED: allocator will fill it in, but use need not.
80 * @GOG_SERIES_OPTIONAL: optional data.
81 * @GOG_SERIES_ERRORS: optional data for error bars.
82 *
83 * Applies to data declarations in #GogSeriesDimDesc.
84 **/
85
86 /**
87 * GogSeriesDesc:
88 * @dim: dimensions descriptions.
89 **/
90
91 /**
92 * GogSeriesDimDesc:
93 * @name: name.
94 * @priority: priority.
95 * @is_shared: whether the dimension is shared among the series.
96 * @val_type: data type.
97 * @ms_type: data type for foreign formats.
98 **/
99
100 /* Keep in sync with GogSeriesFillType enum */
101 static struct {
102 GogSeriesFillType type;
103 char const *name;
104 char const *label;
105 } _fill_type_infos[] = {
106 {GOG_SERIES_FILL_TYPE_Y_ORIGIN, "y-origin", N_("Y origin")},
107 {GOG_SERIES_FILL_TYPE_X_ORIGIN, "x-origin", N_("X origin")},
108 {GOG_SERIES_FILL_TYPE_BOTTOM, "bottom", N_("Bottom")},
109 {GOG_SERIES_FILL_TYPE_LEFT, "left", N_("Left")},
110 {GOG_SERIES_FILL_TYPE_TOP, "top", N_("Top")},
111 {GOG_SERIES_FILL_TYPE_RIGHT, "right", N_("Right")},
112 {GOG_SERIES_FILL_TYPE_ORIGIN, "origin", N_("Origin")},
113 {GOG_SERIES_FILL_TYPE_CENTER, "center", N_("Center")},
114 {GOG_SERIES_FILL_TYPE_EDGE, "edge", N_("Edge")},
115 {GOG_SERIES_FILL_TYPE_SELF, "self", N_("Self")},
116 {GOG_SERIES_FILL_TYPE_NEXT, "next", N_("Next series")},
117 {GOG_SERIES_FILL_TYPE_X_AXIS_MIN, "x-axis-min", N_("X axis minimum")},
118 {GOG_SERIES_FILL_TYPE_X_AXIS_MAX, "x-axis-max", N_("X axis maximum")},
119 {GOG_SERIES_FILL_TYPE_Y_AXIS_MIN, "y-axis-min", N_("Y axis minimum")},
120 {GOG_SERIES_FILL_TYPE_Y_AXIS_MAX, "y-axis-max", N_("Y axis maximum")},
121 {GOG_SERIES_FILL_TYPE_INVALID, "invalid", ""}
122 };
123
124 #ifdef GOFFICE_WITH_GTK
125 #endif
126
127 int gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index);
128
129 /*****************************************************************************/
130 static GObjectClass *gse_parent_klass;
131
132 enum {
133 ELEMENT_PROP_0,
134 ELEMENT_INDEX
135 };
136
element_compare(GogSeriesElement * gse_a,GogSeriesElement * gse_b)137 static gint element_compare (GogSeriesElement *gse_a, GogSeriesElement *gse_b)
138 {
139 return gse_a->index - gse_b->index;
140 }
141
142 static void
gog_series_element_set_index(GogSeriesElement * gse,int ind)143 gog_series_element_set_index (GogSeriesElement *gse, int ind)
144 {
145 gse->index = ind;
146 go_styled_object_apply_theme (GO_STYLED_OBJECT (gse), gse->base.style);
147 go_styled_object_style_changed (GO_STYLED_OBJECT (gse));
148 }
149
150 static void
gog_series_element_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)151 gog_series_element_set_property (GObject *obj, guint param_id,
152 GValue const *value, GParamSpec *pspec)
153 {
154 GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
155 GogObject *gobj = GOG_OBJECT (obj);
156
157 switch (param_id) {
158 case ELEMENT_INDEX :
159 gog_series_element_set_index (gse, g_value_get_int (value));
160 if (gobj->parent != NULL) {
161 GogSeries *series = GOG_SERIES (gobj->parent);
162 series->overrides = g_list_remove (series->overrides, gse);
163 series->overrides = g_list_insert_sorted (series->overrides, gse,
164 (GCompareFunc) element_compare);
165 }
166 break;
167 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
168 return; /* NOTE : RETURN */
169 }
170
171 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
172 }
173
174 static void
gog_series_element_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)175 gog_series_element_get_property (GObject *obj, guint param_id,
176 GValue *value, GParamSpec *pspec)
177 {
178 GogSeriesElement *gse = GOG_SERIES_ELEMENT (obj);
179
180 switch (param_id) {
181 case ELEMENT_INDEX :
182 g_value_set_int (value, gse->index);
183 break;
184 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
185 break;
186 }
187 }
188
189 #ifdef GOFFICE_WITH_GTK
190 static void
cb_index_changed(GtkSpinButton * spin_button,GogSeriesElement * element)191 cb_index_changed (GtkSpinButton *spin_button, GogSeriesElement *element)
192 {
193 int index;
194 int value = gtk_spin_button_get_value (spin_button);
195
196 if ((int) element->index == value)
197 return;
198
199 index = gog_series_get_valid_element_index (
200 GOG_SERIES (gog_object_get_parent (GOG_OBJECT (element))),
201 element->index, value);
202
203 if (index != value)
204 gtk_spin_button_set_value (spin_button, index);
205
206 g_object_set (element, "index", (int) index, NULL);
207 }
208
209 static void
gog_series_element_populate_editor(GogObject * gobj,GOEditor * editor,GogDataAllocator * dalloc,GOCmdContext * cc)210 gog_series_element_populate_editor (GogObject *gobj,
211 GOEditor *editor,
212 GogDataAllocator *dalloc,
213 GOCmdContext *cc)
214 {
215 static guint series_element_pref_page = 1;
216 GtkWidget *w, *gse_grid = NULL, *grid;
217 GogSeriesElementClass *klass = GOG_SERIES_ELEMENT_GET_CLASS (gobj);
218
219 if (klass->gse_populate_editor)
220 gse_grid = (*klass->gse_populate_editor) (gobj, editor, cc);
221
222 (GOG_OBJECT_CLASS(gse_parent_klass)->populate_editor) (gobj, editor, dalloc, cc);
223
224 if (gse_grid == NULL) {
225 grid = gtk_grid_new ();
226 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
227 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
228 gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
229 } else {
230 grid = gse_grid;
231 gtk_grid_insert_row (GTK_GRID (grid), 0);
232 }
233 w = gtk_label_new (_("Index:"));
234 g_object_set (G_OBJECT (w), "xalign", 0., NULL);
235 gtk_grid_attach (GTK_GRID (grid), w, 0, 0, 1, 1);
236 w = gtk_spin_button_new_with_range (0, G_MAXINT, 1);
237 gtk_spin_button_set_value (GTK_SPIN_BUTTON (w),
238 GOG_SERIES_ELEMENT(gobj)->index);
239 g_signal_connect (G_OBJECT (w),
240 "value_changed",
241 G_CALLBACK (cb_index_changed), gobj);
242 gtk_grid_attach (GTK_GRID (grid), w, 1, 0, 1, 1);
243 gtk_widget_show_all (grid);
244
245 if (gse_grid == NULL)
246 go_editor_add_page (editor, grid, _("Settings"));
247
248 go_editor_set_store_page (editor, &series_element_pref_page);
249 }
250 #endif
251
252 static void
gog_series_element_init_style(GogStyledObject * gso,GOStyle * style)253 gog_series_element_init_style (GogStyledObject *gso, GOStyle *style)
254 {
255 GogSeries const *series = GOG_SERIES (GOG_OBJECT (gso)->parent);
256 GOStyle *parent_style;
257
258 g_return_if_fail (series != NULL);
259
260 parent_style = go_styled_object_get_style (GO_STYLED_OBJECT (series));
261 style->interesting_fields = parent_style->interesting_fields;
262 gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
263 style, GOG_OBJECT (gso), GOG_SERIES_ELEMENT (gso)->index,
264 style->interesting_fields);
265 }
266
267 static void
gog_series_element_class_init(GogSeriesElementClass * klass)268 gog_series_element_class_init (GogSeriesElementClass *klass)
269 {
270 GObjectClass *gobject_klass = (GObjectClass *) klass;
271 GogObjectClass *gog_klass = (GogObjectClass *) klass;
272 GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
273 gse_parent_klass = g_type_class_peek_parent (klass);
274
275 gobject_klass->set_property = gog_series_element_set_property;
276 gobject_klass->get_property = gog_series_element_get_property;
277
278 #ifdef GOFFICE_WITH_GTK
279 gog_klass->populate_editor = gog_series_element_populate_editor;
280 #endif
281 style_klass->init_style = gog_series_element_init_style;
282
283 gog_klass->use_parent_as_proxy = TRUE;
284
285 g_object_class_install_property (gobject_klass, ELEMENT_INDEX,
286 g_param_spec_int ("index",
287 _("Index"),
288 _("Index of the corresponding data element"),
289 0, G_MAXINT, 0,
290 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
291 }
292
GSF_CLASS_ABSTRACT(GogSeriesElement,gog_series_element,gog_series_element_class_init,NULL,GOG_TYPE_STYLED_OBJECT)293 GSF_CLASS_ABSTRACT (GogSeriesElement, gog_series_element,
294 gog_series_element_class_init, NULL /*gog_series_element_init*/,
295 GOG_TYPE_STYLED_OBJECT)
296
297 /*****************************************************************************/
298
299 static gboolean
300 regression_curve_can_add (GogObject const *parent)
301 {
302 GogSeries *series = GOG_SERIES (parent);
303 return (series->acceptable_children & GOG_SERIES_ACCEPT_TREND_LINE) != 0;
304 }
305
306 static void
regression_curve_post_add(GogObject * parent,GogObject * child)307 regression_curve_post_add (GogObject *parent, GogObject *child)
308 {
309 gog_object_request_update (child);
310 }
311
312 static void
regression_curve_pre_remove(GogObject * parent,GogObject * child)313 regression_curve_pre_remove (GogObject *parent, GogObject *child)
314 {
315 }
316
317 /*****************************************************************************/
318
319 static GObjectClass *series_parent_klass;
320
321 enum {
322 SERIES_PROP_0,
323 SERIES_PROP_HAS_LEGEND,
324 SERIES_PROP_INTERPOLATION,
325 SERIES_PROP_INTERPOLATION_SKIP_INVALID,
326 SERIES_PROP_FILL_TYPE
327 };
328
329 static gboolean
role_series_element_can_add(GogObject const * parent)330 role_series_element_can_add (GogObject const *parent)
331 {
332 GogSeriesClass *klass = GOG_SERIES_GET_CLASS (parent);
333
334 return ((gog_series_get_valid_element_index(GOG_SERIES (parent), -1, 0) >= 0) &&
335 (klass->series_element_type > 0));
336 }
337
338 static GogObject *
role_series_element_allocate(GogObject * series)339 role_series_element_allocate (GogObject *series)
340 {
341 GogSeriesClass *klass = GOG_SERIES_GET_CLASS (series);
342 GType type = klass->series_element_type;
343 GogObject *gse;
344
345 if (type == 0)
346 return NULL;
347
348 gse = g_object_new (type, NULL);
349 if (gse != NULL)
350 gog_series_element_set_index (GOG_SERIES_ELEMENT (gse),
351 gog_series_get_valid_element_index (GOG_SERIES (series), -1, 0));
352 return gse;
353 }
354
355 static void
role_series_element_post_add(GogObject * parent,GogObject * child)356 role_series_element_post_add (GogObject *parent, GogObject *child)
357 {
358 GogSeries *series = GOG_SERIES (parent);
359 go_styled_object_set_style (GO_STYLED_OBJECT (child),
360 go_styled_object_get_style (GO_STYLED_OBJECT (parent)));
361 series->overrides = g_list_insert_sorted (series->overrides, child,
362 (GCompareFunc) element_compare);
363 }
364
365 static void
role_series_element_pre_remove(GogObject * parent,GogObject * child)366 role_series_element_pre_remove (GogObject *parent, GogObject *child)
367 {
368 GogSeries *series = GOG_SERIES (parent);
369 series->overrides = g_list_remove (series->overrides, child);
370 }
371
372 static gboolean
role_series_labels_can_add(GogObject const * parent)373 role_series_labels_can_add (GogObject const *parent)
374 {
375 GogSeries *series = GOG_SERIES (parent);
376
377 return (series->allowed_pos != 0 && gog_object_get_child_by_name (parent, "Data labels") == NULL);
378 }
379
380 static void
role_series_labels_post_add(GogObject * parent,GogObject * child)381 role_series_labels_post_add (GogObject *parent, GogObject *child)
382 {
383 GogSeries *series = GOG_SERIES (parent);
384 GogSeriesDesc const *desc;
385 unsigned i;
386 GogSeriesLabels *labels = GOG_SERIES_LABELS (child);
387 gog_series_labels_set_allowed_position (labels, series->allowed_pos);
388 gog_series_labels_set_default_position (labels, series->default_pos);
389 /* default is to show values */
390
391 /* Are there any shared dimensions */
392 desc = &series->plot->desc.series;
393 for (i = 0; i < desc->num_dim; i++)
394 if (desc->dim[i].ms_type == GOG_MS_DIM_VALUES)
395 break;
396 labels->format = (i != desc->num_dim)?
397 g_strdup_printf ("%%%u", i):
398 g_strdup ("");
399 labels->supports_percent = GOG_PLOT_GET_CLASS (series->plot)->get_percent != NULL;
400 }
401
402 static void
gog_series_finalize(GObject * obj)403 gog_series_finalize (GObject *obj)
404 {
405 GogSeries *series = GOG_SERIES (obj);
406
407 if (series->values != NULL) {
408 gog_dataset_finalize (GOG_DATASET (obj));
409 g_free (series->values - 1); /* it was aliased */
410 series->values = NULL;
411 }
412
413 g_list_free (series->overrides);
414
415 (*series_parent_klass->finalize) (obj);
416 }
417
418 static void
gog_series_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)419 gog_series_set_property (GObject *obj, guint param_id,
420 GValue const *value, GParamSpec *pspec)
421 {
422 GogSeries *series = GOG_SERIES (obj);
423 gboolean b_tmp;
424 char const *name;
425 unsigned int i;
426
427 switch (param_id) {
428 case SERIES_PROP_HAS_LEGEND :
429 b_tmp = g_value_get_boolean (value);
430 if (series->has_legend ^ b_tmp) {
431 series->has_legend = b_tmp;
432 if (series->plot != NULL)
433 gog_plot_request_cardinality_update (series->plot);
434 }
435 break;
436 case SERIES_PROP_INTERPOLATION:
437 series->interpolation = go_line_interpolation_from_str (g_value_get_string (value));
438 /* check if the interpolation mode is compatible with the plot type ans
439 * replace if not */
440 if (gog_plot_axis_set_pref (gog_series_get_plot (series)) & 1 << GOG_AXIS_RADIAL &&
441 !go_line_interpolation_supports_radial (series->interpolation))
442 series->interpolation = GO_LINE_INTERPOLATION_SPLINE;
443 else if (g_object_get_data (obj, "no-bezier-interpolation") != NULL &&
444 (series->interpolation == GO_LINE_INTERPOLATION_CLOSED_SPLINE ||
445 series->interpolation == GO_LINE_INTERPOLATION_SPLINE ||
446 series->interpolation == GO_LINE_INTERPOLATION_ODF_SPLINE))
447 series->interpolation = GO_LINE_INTERPOLATION_CUBIC_SPLINE;
448 break;
449 case SERIES_PROP_INTERPOLATION_SKIP_INVALID:
450 series->interpolation_skip_invalid = g_value_get_boolean (value);
451 break;
452 case SERIES_PROP_FILL_TYPE:
453 name = g_value_get_string (value);
454 for (i = 0; i < G_N_ELEMENTS (_fill_type_infos); i++)
455 if (strcmp (_fill_type_infos[i].name, name) == 0)
456 series->fill_type = _fill_type_infos[i].type;
457 break;
458
459 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
460 return; /* NOTE : RETURN */
461 }
462
463 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
464 }
465
466 static void
gog_series_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)467 gog_series_get_property (GObject *obj, guint param_id,
468 GValue *value, GParamSpec *pspec)
469 {
470 GogSeries *series = GOG_SERIES (obj);
471
472 switch (param_id) {
473 case SERIES_PROP_HAS_LEGEND :
474 g_value_set_boolean (value, series->has_legend);
475 break;
476 case SERIES_PROP_INTERPOLATION:
477 g_value_set_string (value, go_line_interpolation_as_str (series->interpolation));
478 break;
479 case SERIES_PROP_INTERPOLATION_SKIP_INVALID:
480 g_value_set_boolean (value, series->interpolation_skip_invalid);
481 break;
482 case SERIES_PROP_FILL_TYPE:
483 g_value_set_string (value, _fill_type_infos[series->fill_type].name);
484 break;
485
486 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
487 break;
488 }
489 }
490
491 #ifdef GOFFICE_WITH_GTK
492 static unsigned
make_dim_editor(GtkGrid * grid,unsigned row,GogDataEditor * deditor,char const * name,GogSeriesPriority priority,gboolean is_shared)493 make_dim_editor (GtkGrid *grid, unsigned row, GogDataEditor *deditor,
494 char const *name, GogSeriesPriority priority, gboolean is_shared)
495 {
496 GtkWidget *editor = GTK_WIDGET (deditor);
497 char *txt = g_strdup_printf (
498 ((priority != GOG_SERIES_REQUIRED) ? "(_%s):" : "_%s:"), _(name));
499 GtkWidget *label = gtk_label_new_with_mnemonic (txt);
500 g_free (txt);
501
502 gtk_grid_attach (grid, label,
503 0, row, 1, 1);
504 gtk_widget_set_hexpand (editor, TRUE);
505 gtk_grid_attach (grid, editor,
506 1, row, 1, 1);
507 gtk_label_set_mnemonic_widget (GTK_LABEL (label), editor);
508 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
509
510 go_atk_setup_label (label, editor);
511
512 return row + 1;
513 }
514
515 static void
cb_show_in_legend(GtkToggleButton * b,GObject * series)516 cb_show_in_legend (GtkToggleButton *b, GObject *series)
517 {
518 g_object_set (series,
519 "has-legend", gtk_toggle_button_get_active (b),
520 NULL);
521 }
522
523 static void
cb_line_interpolation_changed(GtkComboBox * box,GogSeries * series)524 cb_line_interpolation_changed (GtkComboBox *box, GogSeries *series)
525 {
526 GtkBuilder *gui = g_object_get_data (G_OBJECT (box), "gui");
527 GtkWidget *widget = GTK_WIDGET (g_object_get_data (G_OBJECT(box), "skip-button"));
528 GtkWidget *grid = go_gtk_builder_get_widget (gui, "clamps-grid");
529 GtkTreeIter iter;
530 gtk_combo_box_get_active_iter (box, &iter);
531 gtk_tree_model_get (gtk_combo_box_get_model (box), &iter, 1, &series->interpolation, -1);
532 gtk_widget_set_sensitive (widget, !go_line_interpolation_auto_skip (series->interpolation));
533 widget = GTK_WIDGET (g_object_get_data (G_OBJECT(box), "fill-type"));
534 if (series->interpolation == GO_LINE_INTERPOLATION_CLAMPED_CUBIC_SPLINE)
535 gtk_widget_show (grid);
536 else
537 gtk_widget_hide (grid);
538 if (widget)
539 gtk_widget_set_sensitive (widget, !go_line_interpolation_auto_skip (series->interpolation));
540 gog_object_emit_changed (GOG_OBJECT (series), FALSE);
541 }
542
543 static void
cb_line_interpolation_skip_changed(GtkToggleButton * btn,GogSeries * series)544 cb_line_interpolation_skip_changed (GtkToggleButton *btn, GogSeries *series)
545 {
546 series->interpolation_skip_invalid = gtk_toggle_button_get_active (btn);
547 gog_object_emit_changed (GOG_OBJECT (series), FALSE);
548 }
549
550 static void
cb_fill_type_changed(GtkComboBox * combo,GObject * obj)551 cb_fill_type_changed (GtkComboBox *combo, GObject *obj)
552 {
553 gog_series_set_fill_type (GOG_SERIES (obj),
554 gog_series_get_fill_type_from_combo (GOG_SERIES (obj), combo));
555 }
556
557 static void
gog_series_populate_editor(GogObject * gobj,GOEditor * editor,GogDataAllocator * dalloc,GOCmdContext * cc)558 gog_series_populate_editor (GogObject *gobj,
559 GOEditor *editor,
560 GogDataAllocator *dalloc,
561 GOCmdContext *cc)
562 {
563 static guint series_pref_page = 1;
564 GtkWidget *w;
565 GtkGrid *grid;
566 unsigned i, row = 0;
567 gboolean has_shared = FALSE;
568 GogSeries *series = GOG_SERIES (gobj);
569 GogSeriesClass *series_class = GOG_SERIES_GET_CLASS (series);
570 GogDataset *set = GOG_DATASET (gobj);
571 GogSeriesDesc const *desc;
572 GogDataType data_type;
573 GtkComboBox *combo = NULL;
574
575 g_return_if_fail (series->plot != NULL);
576
577 /* Are there any shared dimensions */
578 desc = &series->plot->desc.series;
579 for (i = 0; i < desc->num_dim; i++)
580 if (desc->dim[i].is_shared) {
581 has_shared = TRUE;
582 break;
583 }
584
585 w = gtk_grid_new ();
586 grid = GTK_GRID (w);
587 gtk_grid_set_row_spacing (grid, 6);
588 gtk_grid_set_column_spacing (grid, 12);
589 gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
590
591 row = make_dim_editor (grid, row,
592 gog_data_allocator_editor (dalloc, set, -1, GOG_DATA_SCALAR),
593 N_("Name"), TRUE, FALSE);
594
595 /* first the unshared entries */
596 for (i = 0; i < desc->num_dim; i++) {
597 data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
598 GOG_DATA_MATRIX: GOG_DATA_VECTOR;
599 if (!desc->dim[i].is_shared && (desc->dim[i].priority != GOG_SERIES_ERRORS))
600 row = make_dim_editor (grid, row,
601 gog_data_allocator_editor (dalloc, set, i, data_type),
602 desc->dim[i].name, desc->dim[i].priority, FALSE);
603 }
604
605 if (has_shared) {
606 gtk_grid_attach (grid, gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
607 0, row, 2, 1);
608 row++;
609 }
610
611 /* then the shared entries */
612 for (i = 0; i < desc->num_dim; i++) {
613 data_type = (desc->dim[i].val_type == GOG_DIM_MATRIX)?
614 GOG_DATA_MATRIX: GOG_DATA_VECTOR;
615 if (desc->dim[i].is_shared)
616 row = make_dim_editor (grid, row,
617 gog_data_allocator_editor (dalloc, set, i, data_type),
618 desc->dim[i].name, desc->dim[i].priority, TRUE);
619 }
620
621 gtk_grid_attach (grid, gtk_separator_new (GTK_ORIENTATION_HORIZONTAL),
622 0, row, 2, 1);
623 row++;
624 w = gtk_check_button_new_with_mnemonic (_("_Show in Legend"));
625 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
626 gog_series_has_legend (series));
627 g_signal_connect (G_OBJECT (w),
628 "toggled",
629 G_CALLBACK (cb_show_in_legend), series);
630 gtk_grid_attach (grid, w,
631 0, row, 2, 1);
632 gtk_widget_show_all (GTK_WIDGET (grid));
633
634 go_editor_add_page (editor, GTK_WIDGET (grid), _("Data"));
635
636 (GOG_OBJECT_CLASS(series_parent_klass)->populate_editor) (gobj, editor, dalloc, cc);
637
638 grid = GTK_GRID (go_editor_get_registered_widget (editor, "line-grid"));
639 if (grid == NULL)
640 grid = GTK_GRID (go_editor_get_registered_widget (editor, "outline-grid"));
641 if (series_class->has_interpolation && grid != NULL) {
642 GtkBuilder *gui;
643 GtkWidget *widget;
644
645 gui = go_gtk_builder_load_internal ("res:go:graph/gog-series-prefs.ui", GETTEXT_PACKAGE, cc);
646 if (gui != NULL) {
647 unsigned i;
648 GogAxisSet set = gog_plot_axis_set_pref (gog_series_get_plot (series));
649 GogDataset *clamp_set = gog_series_get_interpolation_params (series);
650 GtkListStore *model;
651 GtkTreeIter iter;
652 GtkCellRenderer *renderer;
653 /* FIXME: change when we can filter out non supported modes, see #698100, comment #3 */
654 gboolean no_bezier = g_object_get_data (G_OBJECT (gobj), "no-bezier-interpolation") != NULL;
655 widget = go_gtk_builder_get_widget (gui, "interpolation-prefs");
656 gtk_grid_attach (grid, widget, 0, 3, 4, 1);
657 /* create an interpolation type combo and populate it */
658 combo = GTK_COMBO_BOX (gtk_builder_get_object (gui, "interp-combo"));
659 renderer = gtk_cell_renderer_text_new ();
660 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, FALSE);
661 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
662 "text", 0,
663 NULL);
664 model = GTK_LIST_STORE (gtk_combo_box_get_model (combo));
665 for (i = 0; i < GO_LINE_INTERPOLATION_MAX; i++) {
666 if ((set & 1 << GOG_AXIS_RADIAL && !go_line_interpolation_supports_radial (i)) ||
667 (no_bezier &&
668 (i == GO_LINE_INTERPOLATION_CLOSED_SPLINE ||
669 i == GO_LINE_INTERPOLATION_SPLINE ||
670 i == GO_LINE_INTERPOLATION_ODF_SPLINE)))
671 continue;
672 gtk_list_store_append (model, &iter);
673 gtk_list_store_set (model, &iter,
674 0, _(go_line_interpolation_as_label (i)),
675 1, i, -1);
676 if (i == series->interpolation)
677 gtk_combo_box_set_active_iter (combo, &iter);
678 }
679 g_signal_connect (combo, "changed",
680 G_CALLBACK (cb_line_interpolation_changed), series);
681 w = go_gtk_builder_get_widget (gui, "interpolation-skip-invalid");
682 g_object_set_data (G_OBJECT (combo), "skip-button", w);
683 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), series->interpolation_skip_invalid);
684 if (go_line_interpolation_auto_skip (series->interpolation))
685 gtk_widget_set_sensitive (w, FALSE);
686 g_signal_connect (w, "toggled",
687 G_CALLBACK (cb_line_interpolation_skip_changed), series);
688 gtk_widget_show_all (widget);
689 /* FIXME: a very bad hack to hide this check box for line and area plots */
690 if (no_bezier)
691 gtk_widget_hide (w);
692 if (clamp_set) {
693 GtkWidget *w;
694 widget = go_gtk_builder_get_widget (gui, "clamps-grid");
695 w = GTK_WIDGET (gog_data_allocator_editor (dalloc, clamp_set, 0, GOG_DATA_SCALAR));
696 gtk_widget_set_tooltip_text (w, _("Derivative at first point of the clamped cubic spline."));
697 gtk_widget_show (w);
698 gtk_widget_set_hexpand (w, TRUE);
699 gtk_grid_attach (GTK_GRID (widget), w, 1, 0, 2, 1);
700 w = GTK_WIDGET (gog_data_allocator_editor (dalloc, clamp_set, 1, GOG_DATA_SCALAR));
701 gtk_widget_set_tooltip_text (w, _("Derivative at last point of the clamped cubic spline."));
702 gtk_widget_show (w);
703 gtk_widget_set_hexpand (w, TRUE);
704 gtk_grid_attach (GTK_GRID (widget), w, 1, 1, 2, 1);
705 if (series->interpolation != GO_LINE_INTERPOLATION_CLAMPED_CUBIC_SPLINE)
706 gtk_widget_hide (widget);
707 }
708 g_object_set_data (G_OBJECT (combo), "gui", gui);
709 g_signal_connect_swapped (G_OBJECT (combo), "destroy", G_CALLBACK (g_object_unref), gui);
710 }
711 }
712
713 grid = GTK_GRID (go_editor_get_registered_widget (editor, "fill-grid"));
714 if (series_class->has_fill_type && grid != NULL) {
715 GtkBuilder *gui;
716 GtkWidget *widget;
717
718 gui = go_gtk_builder_load_internal ("res:go:graph/gog-series-prefs.ui", GETTEXT_PACKAGE, cc);
719 if (gui != NULL) {
720 widget = go_gtk_builder_get_widget (gui, "fill_type_combo");
721 gog_series_populate_fill_type_combo (GOG_SERIES (series), GTK_COMBO_BOX (widget));
722 g_signal_connect (G_OBJECT (widget), "changed",
723 G_CALLBACK (cb_fill_type_changed), series);
724 if (combo)
725 g_object_set_data (G_OBJECT (combo), "fill-type", widget);
726 if (series->interpolation == GO_LINE_INTERPOLATION_CLOSED_SPLINE)
727 gtk_widget_set_sensitive (widget, FALSE);
728 widget = go_gtk_builder_get_widget (gui, "fill_type_prefs");
729 gtk_grid_attach (grid, widget, 0, 1, 3, 1);
730 g_object_set_data (G_OBJECT (widget), "gui", gui);
731
732 g_signal_connect_swapped (G_OBJECT (widget), "destroy",
733 G_CALLBACK (g_object_unref), gui);
734 }
735 }
736
737 go_editor_set_store_page (editor, &series_pref_page);
738 }
739 #endif
740
741 static void
gog_series_update(GogObject * obj)742 gog_series_update (GogObject *obj)
743 {
744 GogSeries *series = GOG_SERIES (obj);
745 GogObjectRole const *role = gog_object_find_role_by_name (obj, "Data labels");
746 GSList *l = gog_object_get_children (obj, role), *ptr;
747 series->needs_recalc = FALSE;
748 for (ptr = l; ptr; ptr = ptr->next)
749 gog_object_request_update (ptr->data);
750 g_slist_free (l);
751 }
752
753 static void
gog_series_child_added(GogObject * parent,GogObject * child)754 gog_series_child_added (GogObject *parent, GogObject *child)
755 {
756 if (GOG_IS_TREND_LINE (child))
757 gog_plot_request_cardinality_update (GOG_SERIES (parent)->plot);
758 }
759
760 static void
gog_series_init_style(GogStyledObject * gso,GOStyle * style)761 gog_series_init_style (GogStyledObject *gso, GOStyle *style)
762 {
763 GogSeries const *series = (GogSeries const *)gso;
764 style->interesting_fields = series->plot->desc.series.style_fields;
765 gog_theme_fillin_style (gog_object_get_theme (GOG_OBJECT (gso)),
766 style, GOG_OBJECT (gso), series->index,
767 style->interesting_fields);
768 }
769
770 static void
gog_series_class_init(GogSeriesClass * klass)771 gog_series_class_init (GogSeriesClass *klass)
772 {
773 static GogObjectRole const roles[] = {
774 { N_("Point"), "GogSeriesElement", 0,
775 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
776 role_series_element_can_add, NULL,
777 role_series_element_allocate,
778 role_series_element_post_add,
779 role_series_element_pre_remove, NULL },
780 { N_("Regression curve"), "GogRegCurve", 1,
781 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_TYPE,
782 regression_curve_can_add,
783 NULL,
784 NULL,
785 regression_curve_post_add,
786 regression_curve_pre_remove,
787 NULL },
788 { N_("Trend line"), "GogTrendLine", 2,
789 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_TYPE,
790 regression_curve_can_add,
791 NULL,
792 NULL,
793 regression_curve_post_add,
794 regression_curve_pre_remove,
795 NULL },
796 { N_("Data labels"), "GogSeriesLabels", 3,
797 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
798 role_series_labels_can_add,
799 NULL,
800 NULL,
801 role_series_labels_post_add,
802 NULL,
803 NULL }
804 };
805 unsigned int i;
806
807 GObjectClass *gobject_klass = (GObjectClass *) klass;
808 GogObjectClass *gog_klass = (GogObjectClass *) klass;
809 GogStyledObjectClass *style_klass = (GogStyledObjectClass *) klass;
810
811 series_parent_klass = g_type_class_peek_parent (klass);
812 gobject_klass->finalize = gog_series_finalize;
813 gobject_klass->set_property = gog_series_set_property;
814 gobject_klass->get_property = gog_series_get_property;
815 klass->has_interpolation = FALSE;
816
817 #ifdef GOFFICE_WITH_GTK
818 gog_klass->populate_editor = gog_series_populate_editor;
819 #endif
820 gog_klass->update = gog_series_update;
821 gog_klass->child_added = gog_series_child_added;
822 gog_klass->child_removed = gog_series_child_added;
823 style_klass->init_style = gog_series_init_style;
824 /* series do not have views, so just forward signals from the plot */
825 gog_klass->use_parent_as_proxy = TRUE;
826
827 gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
828
829 g_object_class_install_property (gobject_klass, SERIES_PROP_HAS_LEGEND,
830 g_param_spec_boolean ("has-legend",
831 _("Has-legend"),
832 _("Should the series show up in legends"),
833 TRUE,
834 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
835 g_object_class_install_property (gobject_klass, SERIES_PROP_INTERPOLATION,
836 g_param_spec_string ("interpolation",
837 _("Interpolation"),
838 _("Type of line interpolation"),
839 "linear",
840 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
841 g_object_class_install_property (gobject_klass, SERIES_PROP_INTERPOLATION_SKIP_INVALID,
842 g_param_spec_boolean ("interpolation-skip-invalid",
843 _("Interpolation skip invalid"),
844 _("Should the series interpolation ignore the invalid data"),
845 FALSE,
846 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
847 g_object_class_install_property (gobject_klass, SERIES_PROP_FILL_TYPE,
848 g_param_spec_string ("fill-type",
849 _("Fill type"),
850 _("How to fill the area"),
851 "invalid",
852 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
853
854 klass->valid_fill_type_list = NULL;
855
856 /* Check for consistency between enum and infos */
857 for (i = 0; i < G_N_ELEMENTS (_fill_type_infos); i++)
858 g_assert (_fill_type_infos[i].type == i);
859 }
860
861 static void
gog_series_init(GogSeries * series)862 gog_series_init (GogSeries *series)
863 {
864 series->is_valid = FALSE;
865 series->has_legend = TRUE;
866 series->plot = NULL;
867 series->values = NULL;
868 series->index = -1;
869 series->acceptable_children = 0;
870 series->interpolation = GO_LINE_INTERPOLATION_LINEAR;
871 series->default_pos = GOG_SERIES_LABELS_CENTERED;
872 }
873
874 static void
gog_series_dataset_dims(GogDataset const * set,int * first,int * last)875 gog_series_dataset_dims (GogDataset const *set, int *first, int *last)
876 {
877 GogSeries const *series = GOG_SERIES (set);
878 *first = -1;
879 if (series->plot == NULL || series->values == NULL)
880 *last = -2;
881 else
882 *last = (series->plot->desc.series.num_dim - 1);
883 }
884
885 static GogDatasetElement *
gog_series_dataset_get_elem(GogDataset const * set,int dim_i)886 gog_series_dataset_get_elem (GogDataset const *set, int dim_i)
887 {
888 GogSeries const *series = GOG_SERIES (set);
889
890 g_return_val_if_fail (dim_i >= -1, NULL);
891
892 if (dim_i >= (int)series->plot->desc.series.num_dim)
893 return NULL;
894 return series->values + dim_i;
895 }
896
897 static void
gog_series_dataset_set_dim(GogDataset * set,int dim_i,GOData * val,GError ** err)898 gog_series_dataset_set_dim (GogDataset *set, int dim_i,
899 GOData *val, GError **err)
900 {
901 GogSeriesDesc const *desc;
902 GogSeries *series = GOG_SERIES (set);
903 GogGraph *graph = gog_object_get_graph (GOG_OBJECT (series));
904
905 g_return_if_fail (GOG_IS_PLOT (series->plot));
906 g_return_if_fail (dim_i >= -1);
907
908 if (dim_i < 0) {
909 char *name = NULL;
910 if (NULL != series->values[-1].data)
911 name = go_data_get_scalar_string (series->values[-1].data);
912 gog_object_set_name (GOG_OBJECT (series), name, err);
913 return;
914 }
915
916 gog_series_check_validity (series);
917
918 /* clone shared dimensions into other series in the plot, and
919 * invalidate if necessary */
920 desc = &series->plot->desc.series;
921 g_return_if_fail (dim_i < (int) desc->num_dim);
922
923 if (desc->dim[dim_i].is_shared) {
924 GSList *ptr = series->plot->series;
925
926 val = series->values[dim_i].data;
927 for (; ptr != NULL ; ptr = ptr->next) {
928 gog_dataset_set_dim_internal (GOG_DATASET (ptr->data),
929 dim_i, val, graph);
930 gog_series_check_validity (GOG_SERIES (ptr->data));
931 }
932 }
933 }
934
935 static void
gog_series_dataset_dim_changed(GogDataset * set,int dim_i)936 gog_series_dataset_dim_changed (GogDataset *set, int dim_i)
937 {
938 GogSeries *series = GOG_SERIES (set);
939
940 if (dim_i >= 0) {
941 GogSeriesClass *klass = GOG_SERIES_GET_CLASS (set);
942 GogPlot *plot = GOG_PLOT (GOG_OBJECT (set)->parent);
943 /* FIXME: we probaly need a signal which will be connected
944 * to axis and legend objects and let them check if resize
945 * is really needed (similar to child-name-changed
946 * connected to legend). For now, let resize for every label
947 * change */
948 gboolean resize = plot != NULL ?
949 plot->desc.series.dim[dim_i].val_type == GOG_DIM_LABEL :
950 FALSE;
951
952 if (!series->needs_recalc) {
953 series->needs_recalc = TRUE;
954 gog_object_emit_changed (GOG_OBJECT (set), resize);
955 }
956 if (klass->dim_changed != NULL)
957 (klass->dim_changed) (GOG_SERIES (set), dim_i);
958
959 gog_object_request_update (GOG_OBJECT (set));
960 } else {
961 GOData *name_src = series->values[-1].data;
962 char *name = (name_src != NULL)
963 ? go_data_get_scalar_string (name_src) : NULL;
964 gog_object_set_name (GOG_OBJECT (set), name, NULL);
965 }
966 gog_series_check_validity (series);
967 }
968
969 static void
gog_series_dataset_init(GogDatasetClass * iface)970 gog_series_dataset_init (GogDatasetClass *iface)
971 {
972 iface->get_elem = gog_series_dataset_get_elem;
973 iface->set_dim = gog_series_dataset_set_dim;
974 iface->dims = gog_series_dataset_dims;
975 iface->dim_changed = gog_series_dataset_dim_changed;
976 }
977
GSF_CLASS_FULL(GogSeries,gog_series,NULL,NULL,gog_series_class_init,NULL,gog_series_init,GOG_TYPE_STYLED_OBJECT,G_TYPE_FLAG_ABSTRACT,GSF_INTERFACE (gog_series_dataset_init,GOG_TYPE_DATASET))978 GSF_CLASS_FULL (GogSeries, gog_series,
979 NULL, NULL, gog_series_class_init, NULL,
980 gog_series_init, GOG_TYPE_STYLED_OBJECT, G_TYPE_FLAG_ABSTRACT,
981 GSF_INTERFACE (gog_series_dataset_init, GOG_TYPE_DATASET))
982
983 /**
984 * gog_series_get_plot:
985 * @series: #GogSeries
986 *
987 * Returns: (transfer none): the possibly NULL plot that contains this series.
988 **/
989 GogPlot *
990 gog_series_get_plot (GogSeries const *series)
991 {
992 g_return_val_if_fail (GOG_IS_SERIES (series), NULL);
993 return series->plot;
994 }
995
996 /**
997 * gog_series_is_valid:
998 * @series: #GogSeries
999 *
1000 * Returns: the current cached validity. Does not recheck
1001 **/
1002 gboolean
gog_series_is_valid(GogSeries const * series)1003 gog_series_is_valid (GogSeries const *series)
1004 {
1005 g_return_val_if_fail (GOG_IS_SERIES (series), FALSE);
1006 return series->is_valid;
1007 }
1008
1009 /**
1010 * gog_series_check_validity:
1011 * @series: #GogSeries
1012 *
1013 * Updates the is_valid flag for a series.
1014 * This is an internal utility that should not really be necessary for general
1015 * usage.
1016 **/
1017 void
gog_series_check_validity(GogSeries * series)1018 gog_series_check_validity (GogSeries *series)
1019 {
1020 unsigned i;
1021 GogSeriesDesc const *desc;
1022
1023 g_return_if_fail (GOG_IS_SERIES (series));
1024 g_return_if_fail (GOG_IS_PLOT (series->plot));
1025
1026 desc = &series->plot->desc.series;
1027 for (i = series->plot->desc.series.num_dim; i-- > 0; )
1028 if (desc->dim[i].priority == GOG_SERIES_REQUIRED && (
1029 series->values[i].data == NULL ||
1030 !go_data_has_value (series->values[i].data))) {
1031 series->is_valid = FALSE;
1032 return;
1033 }
1034 series->is_valid = TRUE;
1035 }
1036
1037 /**
1038 * gog_series_has_legend:
1039 * @series: #GogSeries
1040 *
1041 * Returns: TRUE if the series has a visible legend entry
1042 **/
1043 gboolean
gog_series_has_legend(GogSeries const * series)1044 gog_series_has_legend (GogSeries const *series)
1045 {
1046 g_return_val_if_fail (GOG_IS_SERIES (series), FALSE);
1047 return series->has_legend;
1048 }
1049
1050 /**
1051 * gog_series_set_index:
1052 * @series: #GogSeries
1053 * @ind: >= 0 assigns a new index, < 0 resets to auto
1054 * @is_manual: gboolean
1055 *
1056 * If @ind >= 0 attempt to assign the new index. Auto
1057 * indicies (@is_manual == FALSE) will not override the current
1058 * index if it is manual. An @index < 0, will reset the index to
1059 * automatic and potentially queue a revaluation of the parent
1060 * chart's cardinality.
1061 **/
1062 void
gog_series_set_index(GogSeries * series,int ind,gboolean is_manual)1063 gog_series_set_index (GogSeries *series, int ind, gboolean is_manual)
1064 {
1065 g_return_if_fail (GOG_IS_SERIES (series));
1066
1067 if (ind < 0) {
1068 if (series->manual_index && series->plot != NULL)
1069 gog_plot_request_cardinality_update (series->plot);
1070 series->manual_index = FALSE;
1071 return;
1072 }
1073
1074 if (is_manual)
1075 series->manual_index = TRUE;
1076 else if (series->manual_index)
1077 return;
1078
1079 series->index = ind;
1080 go_styled_object_apply_theme (GO_STYLED_OBJECT (series), series->base.style);
1081 go_styled_object_style_changed (GO_STYLED_OBJECT (series));
1082 }
1083
1084 /**
1085 * gog_series_get_name:
1086 * @series: a #GogSeries
1087 *
1088 * Gets the _source_ of the name associated with the series.
1089 * NOTE : this is _NOT_ the actual name.
1090 *
1091 * return value: (transfer none): a #GODataScalar, without added reference.
1092 **/
1093 GOData *
gog_series_get_name(GogSeries const * series)1094 gog_series_get_name (GogSeries const *series)
1095 {
1096 g_return_val_if_fail (GOG_IS_SERIES (series), NULL);
1097
1098 return series->values[-1].data;
1099 }
1100
1101 /**
1102 * gog_series_set_name:
1103 * @series: a #GogSeries
1104 * @name_src: (transfer full): a #GODataScalar
1105 * @err: a #GError
1106 *
1107 * Absorbs a ref to @name_src.
1108 *
1109 **/
1110 void
gog_series_set_name(GogSeries * series,GODataScalar * name_src,GError ** err)1111 gog_series_set_name (GogSeries *series, GODataScalar *name_src, GError **err)
1112 {
1113 gog_dataset_set_dim (GOG_DATASET (series), -1, GO_DATA (name_src), err);
1114 }
1115
1116 /**
1117 * gog_series_set_dim:
1118 * @series: #GogSeries
1119 * @dim_i: Which dimension
1120 * @val: (transfer full): #GOData
1121 * @err: (allow-none): optional #GError pointer
1122 *
1123 * Absorbs a ref to @val
1124 **/
1125 void
gog_series_set_dim(GogSeries * series,int dim_i,GOData * val,GError ** err)1126 gog_series_set_dim (GogSeries *series, int dim_i, GOData *val, GError **err)
1127 {
1128 gog_dataset_set_dim (GOG_DATASET (series), dim_i, val, err);
1129 }
1130
1131 int
gog_series_map_XL_dim(GogSeries const * series,GogMSDimType ms_type)1132 gog_series_map_XL_dim (GogSeries const *series, GogMSDimType ms_type)
1133 {
1134 GogSeriesDesc const *desc = &series->plot->desc.series;
1135 unsigned i = desc->num_dim;
1136
1137 if (ms_type == GOG_MS_DIM_LABELS)
1138 return -1;
1139 while (i-- > 0)
1140 if (desc->dim[i].ms_type == ms_type)
1141 return i;
1142 return -2;
1143 }
1144
1145 void
gog_series_set_XL_dim(GogSeries * series,GogMSDimType ms_type,GOData * val,GError ** err)1146 gog_series_set_XL_dim (GogSeries *series, GogMSDimType ms_type, GOData *val, GError **err)
1147 {
1148 int dim;
1149 g_return_if_fail (series !=NULL);
1150 dim = gog_series_map_XL_dim (series, ms_type);
1151 if (dim >= -1) {
1152 gog_series_set_dim (series, dim, val, err);
1153 return;
1154 }
1155 g_object_unref (val);
1156 }
1157
1158 /**
1159 * gog_series_num_elements:
1160 * @series: #GogSeries
1161 *
1162 * Returns: the number of elements in the series
1163 **/
1164 unsigned
gog_series_num_elements(GogSeries const * series)1165 gog_series_num_elements (GogSeries const *series)
1166 {
1167 return series->num_elements;
1168 }
1169
1170 /**
1171 * gog_series_get_overrides:
1172 * @series: #GogSeries
1173 *
1174 * Overrides are data owning their own style, overriding the series style.
1175 * Returns: (element-type GogSeriesElement) (transfer none): the series
1176 * overrides.
1177 **/
1178 GList const *
gog_series_get_overrides(GogSeries const * series)1179 gog_series_get_overrides (GogSeries const *series)
1180 {
1181 return series->overrides;
1182 }
1183
1184 int
gog_series_get_valid_element_index(GogSeries const * series,int old_index,int desired_index)1185 gog_series_get_valid_element_index (GogSeries const *series, int old_index, int desired_index)
1186 {
1187 int index;
1188 GList *ptr;
1189
1190 g_return_val_if_fail (GOG_IS_SERIES (series), -1);
1191
1192 if ((desired_index >= (int) series->num_elements) ||
1193 (desired_index < 0))
1194 return old_index;
1195
1196 if (desired_index > old_index)
1197 for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
1198 index = GOG_SERIES_ELEMENT (ptr->data)->index;
1199 if (index > desired_index)
1200 break;
1201 if (index == desired_index)
1202 desired_index++;
1203 }
1204 else
1205 for (ptr = g_list_last (series->overrides); ptr != NULL; ptr = ptr->prev) {
1206 index = GOG_SERIES_ELEMENT (ptr->data)->index;
1207 if (index < desired_index)
1208 break;
1209 if (index == desired_index)
1210 desired_index--;
1211 }
1212
1213 if ((desired_index >= 0) &&
1214 (desired_index < (int) series->num_elements))
1215 return desired_index;
1216
1217 return old_index;
1218 }
1219
1220 /**
1221 * gog_series_get_element:
1222 * @series: #GogSeries
1223 * @index: the element index
1224 *
1225 * Returns: (transfer none): the #GogSeriesElement corresponding to @index if
1226 * any
1227 **/
1228 GogSeriesElement *
gog_series_get_element(GogSeries const * series,int index)1229 gog_series_get_element (GogSeries const *series, int index)
1230 {
1231 GList *ptr;
1232 GogSeriesElement *element;
1233
1234 g_return_val_if_fail (GOG_IS_SERIES (series), NULL);
1235
1236 for (ptr = series->overrides; ptr != NULL; ptr = ptr->next) {
1237 element = GOG_SERIES_ELEMENT (ptr->data);
1238 if ((int) element->index == index)
1239 return element;
1240 }
1241
1242 return NULL;
1243 }
1244
1245 static unsigned int
gog_series_get_data(GogSeries const * series,int * indices,double ** data,int n_vectors)1246 gog_series_get_data (GogSeries const *series, int *indices, double **data, int n_vectors)
1247 {
1248 GOData *vector;
1249 int i, n_points = 0, vector_n_points;
1250 int first, last;
1251 int index;
1252 gboolean is_set = FALSE;
1253
1254 if (!gog_series_is_valid (series))
1255 return 0;
1256
1257 gog_dataset_dims (GOG_DATASET (series), &first, &last);
1258
1259 for (i = 0; i < n_vectors; i++) {
1260 index = indices != NULL ? indices[i] : i;
1261 if (index >= first && index <= last &&
1262 (vector = series->values[index].data) != NULL) {
1263 data[i] = (go_data_has_value (vector))? go_data_get_values (vector): NULL;
1264 vector_n_points = go_data_get_vector_size (vector);
1265 if (!is_set) {
1266 is_set = TRUE;
1267 n_points = vector_n_points;
1268 } else
1269 n_points = MIN (vector_n_points, n_points);
1270 } else
1271 data[i] = NULL;
1272 }
1273
1274 return n_points;
1275 }
1276
1277 unsigned
gog_series_get_xy_data(GogSeries const * series,double const ** x,double const ** y)1278 gog_series_get_xy_data (GogSeries const *series,
1279 double const **x,
1280 double const **y)
1281 {
1282 GogSeriesClass *klass = GOG_SERIES_GET_CLASS (series);
1283 double *data[2];
1284 unsigned int n_points;
1285 int first, last;
1286
1287 g_return_val_if_fail (klass != NULL, 0);
1288
1289 if (klass->get_xy_data != NULL) {
1290 if (!gog_series_is_valid (GOG_SERIES (series)))
1291 return 0;
1292
1293 return (klass->get_xy_data) (series, x, y);
1294 }
1295
1296 gog_dataset_dims (GOG_DATASET (series), &first, &last);
1297
1298 g_return_val_if_fail (first <= 0, 0);
1299 g_return_val_if_fail (last >= 1, 0);
1300
1301 n_points = gog_series_get_data (series, NULL, data, 2);
1302
1303 *x = data[0];
1304 *y = data[1];
1305
1306 return n_points;
1307 }
1308
1309 unsigned
gog_series_get_xyz_data(GogSeries const * series,double const ** x,double const ** y,double const ** z)1310 gog_series_get_xyz_data (GogSeries const *series,
1311 double const **x,
1312 double const **y,
1313 double const **z)
1314 {
1315 double *data[3];
1316 unsigned int n_points;
1317
1318 n_points = gog_series_get_data (series, NULL, data, 3);
1319
1320 *x = data[0];
1321 *y = data[1];
1322 *z = data[2];
1323
1324 return n_points;
1325 }
1326
1327 GogSeriesFillType
gog_series_get_fill_type(GogSeries const * series)1328 gog_series_get_fill_type (GogSeries const *series)
1329 {
1330 g_return_val_if_fail (GOG_IS_SERIES (series), GOG_SERIES_FILL_TYPE_INVALID);
1331
1332 return series->fill_type;
1333 }
1334
1335 void
gog_series_set_fill_type(GogSeries * series,GogSeriesFillType fill_type)1336 gog_series_set_fill_type (GogSeries *series, GogSeriesFillType fill_type)
1337 {
1338 GogSeriesClass *series_klass;
1339
1340 g_return_if_fail (GOG_IS_SERIES (series));
1341 if (series->fill_type == fill_type)
1342 return;
1343 g_return_if_fail (fill_type >= 0 && fill_type < GOG_SERIES_FILL_TYPE_INVALID);
1344
1345 series_klass = GOG_SERIES_GET_CLASS (series);
1346 g_return_if_fail (series_klass->valid_fill_type_list != NULL);
1347
1348 series->fill_type = fill_type;
1349 gog_object_request_update (GOG_OBJECT (series));
1350 }
1351
1352 #ifdef GOFFICE_WITH_GTK
1353 void
gog_series_populate_fill_type_combo(GogSeries const * series,GtkComboBox * combo)1354 gog_series_populate_fill_type_combo (GogSeries const *series, GtkComboBox *combo)
1355 {
1356 GogSeriesClass *series_klass;
1357 GogSeriesFillType fill_type;
1358 unsigned int i;
1359
1360 g_return_if_fail (GOG_IS_SERIES (series));
1361 series_klass = GOG_SERIES_GET_CLASS (series);
1362 g_return_if_fail (series_klass->valid_fill_type_list != NULL);
1363
1364 gtk_list_store_clear (GTK_LIST_STORE (gtk_combo_box_get_model (combo)));
1365 for (i = 0; series_klass->valid_fill_type_list[i] != GOG_SERIES_FILL_TYPE_INVALID; i++) {
1366 fill_type = series_klass->valid_fill_type_list[i];
1367 if (fill_type < GOG_SERIES_FILL_TYPE_INVALID) {
1368 go_gtk_combo_box_append_text (combo, _(_fill_type_infos[fill_type].label));
1369 if (fill_type == series->fill_type)
1370 gtk_combo_box_set_active (combo, i);
1371 }
1372 }
1373 }
1374
1375 GogSeriesFillType
gog_series_get_fill_type_from_combo(GogSeries const * series,GtkComboBox * combo)1376 gog_series_get_fill_type_from_combo (GogSeries const *series, GtkComboBox *combo)
1377 {
1378 GogSeriesClass *series_klass;
1379
1380 g_return_val_if_fail (GOG_IS_SERIES (series), GOG_SERIES_FILL_TYPE_INVALID);
1381 series_klass = GOG_SERIES_GET_CLASS (series);
1382 g_return_val_if_fail (series_klass->valid_fill_type_list != NULL, GOG_SERIES_FILL_TYPE_INVALID);
1383
1384 return series_klass->valid_fill_type_list[gtk_combo_box_get_active (combo)];
1385 }
1386 #endif
1387
1388 /**
1389 * gog_series_get_interpolation_params:
1390 * @series: #GogSeries
1391 *
1392 * Only constrained cubic spline interpolation use parameters (for the
1393 * slopes at each end).
1394 * Returns: (transfer none): the interpolation parameters.
1395 **/
1396 GogDataset *
gog_series_get_interpolation_params(GogSeries const * series)1397 gog_series_get_interpolation_params (GogSeries const *series)
1398 {
1399 GogSeriesClass *series_klass;
1400
1401 g_return_val_if_fail (GOG_IS_SERIES (series), NULL);
1402 series_klass = GOG_SERIES_GET_CLASS (series);
1403 return (series_klass->get_interpolation_params)?
1404 series_klass->get_interpolation_params (series):
1405 NULL;
1406 }
1407