1 /*
2 * gog-histogram.c
3 *
4 * Copyright (C) 2005-2010 Jean Brefort (jean.brefort@normalesup.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 "gog-histogram.h"
24 #include <goffice/data/go-data.h>
25 #include <goffice/graph/gog-axis.h>
26 #include <goffice/graph/gog-chart.h>
27 #include <goffice/graph/gog-chart-map.h>
28 #include <goffice/graph/gog-renderer.h>
29 #include <goffice/graph/gog-series-lines.h>
30 #include <goffice/math/go-math.h>
31 #include <goffice/utils/go-format.h>
32 #include <goffice/utils/go-path.h>
33 #include <goffice/utils/go-persist.h>
34 #include <goffice/utils/go-styled-object.h>
35 #include <glib/gi18n-lib.h>
36 #include <gsf/gsf-impl-utils.h>
37
38 #define GOG_TYPE_HISTOGRAM_PLOT_SERIES (gog_histogram_plot_series_get_type ())
39 #define GOG_HISTOGRAM_PLOT_SERIES(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_HISTOGRAM_PLOT_SERIES, GogHistogramPlotSeries))
40 #define GOG_IS_HISTOGRAM_PLOT_SERIES(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_HISTOGRAM_PLOT_SERIES))
41
42 typedef struct {
43 GogSeries base;
44 GogObject *droplines;
45 double *x, *y, *y_; /* y_ is the second data for double histograms. */
46 double *real_x, *real_y, *real_y_;
47 } GogHistogramPlotSeries;
48 typedef GogSeriesClass GogHistogramPlotSeriesClass;
49
50 GType gog_histogram_plot_series_get_type (void);
51 GType gog_histogram_plot_view_get_type (void);
52
53 static GogObjectClass *histogram_plot_parent_klass;
54
55 /* Tests if product is strictly positive without causing overflow. */
56 static gboolean
pos_product(double x1,double x2)57 pos_product (double x1, double x2)
58 {
59 return ((x1 > 0 && x2 > 0) ||
60 (x1 < 0 && x2 < 0));
61 }
62
63
64 static void
gog_histogram_plot_clear_formats(GogHistogramPlot * model)65 gog_histogram_plot_clear_formats (GogHistogramPlot *model)
66 {
67 go_format_unref (model->x.fmt);
68 model->x.fmt = NULL;
69
70 go_format_unref (model->y.fmt);
71 model->y.fmt = NULL;
72 }
73
74 static char const *
gog_histogram_plot_type_name(G_GNUC_UNUSED GogObject const * item)75 gog_histogram_plot_type_name (G_GNUC_UNUSED GogObject const *item)
76 {
77 /* xgettext : the base for how to name histogram-plot objects
78 * eg The 2nd histogram-plot in a chart will be called
79 * Histogram2 */
80 return N_("Histogram");
81 }
82
83 static void
gog_histogram_plot_update(GogObject * obj)84 gog_histogram_plot_update (GogObject *obj)
85 {
86 GogHistogramPlot *model = GOG_HISTOGRAM_PLOT (obj);
87 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (
88 model->base.series->data);
89 double x_min, x_max, y_min = DBL_MAX, y_max = -DBL_MAX, *x_vals = NULL, *y_vals = NULL, val;
90 unsigned i, y_len = 0;
91
92 if (!gog_series_is_valid (GOG_SERIES (series)) || series->base.num_elements == 0)
93 return;
94
95 g_free (series->x);
96 series->x = g_new (double, series->base.num_elements);
97 if (series->real_x != NULL)
98 x_vals = series->real_x;
99 else if (series->base.values[0].data != NULL)
100 x_vals = go_data_get_values (series->base.values[0].data);
101 if (x_vals != NULL) {
102 x_min = x_vals[0];
103 x_max = x_vals[series->base.num_elements];
104 if (series->base.values[0].data) {
105 if (model->x.fmt == NULL)
106 model->x.fmt = go_data_preferred_fmt (series->base.values[0].data);
107 model->x.date_conv = go_data_date_conv (series->base.values[0].data);
108 }
109 for (i = 0; i < series->base.num_elements; i++)
110 series->x[i] = (x_vals[i] + x_vals[i+1]) / 2;
111 } else {
112 x_vals = NULL;
113 x_min = 0;
114 x_max = series->base.num_elements;
115 for (i = 0; i < series->base.num_elements; i++)
116 series->x[i] = (double) i + 0.5;
117 }
118 if (model->x.minima != x_min || model->x.maxima != x_max) {
119 model->x.minima = x_min;
120 model->x.maxima = x_max;
121 gog_axis_bound_changed (model->base.axis[model->vertical? 0: 1], GOG_OBJECT (model));
122 }
123 g_free (series->y);
124 series->y = NULL;
125 if (series->real_y != NULL) {
126 y_vals = series->real_y;
127 y_len = series->base.num_elements;
128 } else if (series->base.values[1].data != NULL) {
129 y_vals = go_data_get_values (series->base.values[1].data);
130 y_len = go_data_get_vector_size (series->base.values[1].data);
131 if (y_len > series->base.num_elements)
132 y_len = series->base.num_elements;
133 }
134 if (y_vals != NULL) {
135 double sum = 0.;
136 series->y = g_new0 (double, series->base.num_elements);
137 for (i = 0; i < y_len; i++)
138 if (go_finite (y_vals[i])) {
139 if (model->cumulative)
140 sum += y_vals[i];
141 else
142 sum = y_vals[i];
143 series->y[i] = val = sum / (x_vals[i+1] - x_vals[i]);
144 if (val < y_min)
145 y_min = val;
146 if (val > y_max)
147 y_max = val;
148 } else
149 series->y[i] = model->cumulative ? sum : 0.;
150 if (model->y.fmt == NULL)
151 model->y.fmt = go_data_preferred_fmt (series->base.values[1].data);
152 model->y.date_conv = go_data_date_conv (series->base.values[1].data);
153 }
154 if (GOG_IS_DOUBLE_HISTOGRAM_PLOT (model) && series->base.values[2].data != NULL) {
155 double max = 0.;
156 g_free (series->y_);
157 series->y_ = NULL;
158 if (series->real_y_ != NULL) {
159 y_vals = series->real_y_;
160 y_len = series->base.num_elements;
161 } else if (series->base.values[1].data != NULL) {
162 y_vals = go_data_get_values (series->base.values[1].data);
163 y_len = go_data_get_vector_size (series->base.values[1].data);
164 if (y_len > series->base.num_elements)
165 y_len = series->base.num_elements;
166 } else
167 y_vals = NULL;
168 if (y_vals != NULL) {
169 double sum = 0.;
170 y_min = 0.;
171 series->y_ = g_new0 (double, series->base.num_elements);
172 for (i = 0; i < y_len; i++)
173 if (go_finite (y_vals[i])) {
174 if (model->cumulative)
175 sum += y_vals[i];
176 else
177 sum = y_vals[i];
178 series->y_[i] = val = -sum / (x_vals[i+1] - x_vals[i]);
179 if (val < y_min)
180 y_min = val;
181 if (val > max)
182 max = val;
183 } else
184 series->y_[i] = model->cumulative ? sum : 0.;
185 }
186 if (y_max < 0)
187 y_max = max;
188 }
189 if (y_min > y_max)
190 y_min = y_max = go_nan;
191 if (model->y.minima != y_min || model->y.maxima != y_max) {
192 model->y.minima = y_min;
193 model->y.maxima = y_max;
194 gog_axis_bound_changed (model->base.axis[model->vertical? 1: 0], GOG_OBJECT (model));
195 }
196 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
197 }
198
199 static GOData *
gog_histogram_plot_axis_get_bounds(GogPlot * plot,GogAxisType axis,GogPlotBoundInfo * bounds)200 gog_histogram_plot_axis_get_bounds (GogPlot *plot, GogAxisType axis,
201 GogPlotBoundInfo *bounds)
202 {
203 GogHistogramPlot *model = GOG_HISTOGRAM_PLOT (plot);
204
205
206 if ((axis == GOG_AXIS_X && model->vertical) || (axis == GOG_AXIS_Y && !model->vertical)) {
207 bounds->val.minima = model->x.minima;
208 bounds->val.maxima = model->x.maxima;
209 if (bounds->fmt == NULL && model->x.fmt != NULL)
210 bounds->fmt = go_format_ref (model->x.fmt);
211 if (model->x.date_conv)
212 bounds->date_conv = model->x.date_conv;
213 } else {
214 bounds->val.minima = model->y.minima;
215 bounds->val.maxima = model->y.maxima;
216 if (bounds->fmt == NULL && model->y.fmt != NULL)
217 bounds->fmt = go_format_ref (model->y.fmt);
218 if (model->y.date_conv)
219 bounds->date_conv = model->y.date_conv;
220 }
221 bounds->is_discrete = FALSE;
222 return NULL;
223 }
224
225 enum {
226 HISTOGRAM_PROP_0,
227 HISTOGRAM_PROP_VERTICAL,
228 HISTOGRAM_PROP_CUMULATIVE,
229 HISTOGRAM_PROP_BEFORE_GRID
230 };
231
232 static void
gog_histogram_plot_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)233 gog_histogram_plot_get_property (GObject *obj, guint param_id,
234 GValue *value, GParamSpec *pspec)
235 {
236 GogHistogramPlot *model = GOG_HISTOGRAM_PLOT (obj);
237 switch (param_id) {
238 case HISTOGRAM_PROP_VERTICAL:
239 g_value_set_boolean (value, model->vertical);
240 break;
241 case HISTOGRAM_PROP_CUMULATIVE:
242 g_value_set_boolean (value, model->cumulative);
243 break;
244 case HISTOGRAM_PROP_BEFORE_GRID:
245 g_value_set_boolean (value, GOG_PLOT (obj)->rendering_order == GOG_PLOT_RENDERING_BEFORE_GRID);
246 break;
247
248 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
249 break;
250 }
251 }
252
253 static void
gog_histogram_plot_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)254 gog_histogram_plot_set_property (GObject *obj, guint param_id,
255 GValue const *value, GParamSpec *pspec)
256 {
257 GogHistogramPlot *model = GOG_HISTOGRAM_PLOT (obj);
258 switch (param_id) {
259 case HISTOGRAM_PROP_VERTICAL:
260 if (g_value_get_boolean (value) != model->vertical) {
261 model->vertical = !model->vertical;
262 /* force axis bounds reevaluation */
263 model->x.minima = model->y.minima = G_MAXDOUBLE;
264 gog_object_request_update (GOG_OBJECT (model));
265 }
266 break;
267 case HISTOGRAM_PROP_CUMULATIVE:
268 if (g_value_get_boolean (value) != model->cumulative) {
269 model->cumulative = !model->cumulative;
270 gog_object_request_update (GOG_OBJECT (model));
271 }
272 break;
273 case HISTOGRAM_PROP_BEFORE_GRID:
274 GOG_PLOT (obj)->rendering_order =
275 (g_value_get_boolean (value)
276 ? GOG_PLOT_RENDERING_BEFORE_GRID
277 : GOG_PLOT_RENDERING_LAST);
278 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
279 break;
280
281 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
282 return; /* NOTE : RETURN */
283 }
284 }
285
286 #ifdef GOFFICE_WITH_GTK
287 #include <goffice/gtk/goffice-gtk.h>
288
289 static void
vertical_changed_cb(GtkToggleButton * btn,GogHistogramPlot * model)290 vertical_changed_cb (GtkToggleButton *btn, GogHistogramPlot *model)
291 {
292 if (gtk_toggle_button_get_active (btn) != model->vertical) {
293 model->vertical = !model->vertical;
294 gog_object_request_update (GOG_OBJECT (model));
295 /* force axis bounds reevaluation */
296 model->x.minima = model->y.minima = G_MAXDOUBLE;
297 }
298 }
299
300 static void
cumulative_changed_cb(GtkToggleButton * btn,GogHistogramPlot * model)301 cumulative_changed_cb (GtkToggleButton *btn, GogHistogramPlot *model)
302 {
303 if (gtk_toggle_button_get_active (btn) != model->cumulative) {
304 model->cumulative = !model->cumulative;
305 gog_object_request_update (GOG_OBJECT (model));
306 }
307 }
308
309 static void
display_before_grid_cb(GtkToggleButton * btn,GObject * obj)310 display_before_grid_cb (GtkToggleButton *btn, GObject *obj)
311 {
312 g_object_set (obj, "before-grid", gtk_toggle_button_get_active (btn), NULL);
313 }
314
315 static void
gog_histogram_plot_populate_editor(GogObject * item,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)316 gog_histogram_plot_populate_editor (GogObject *item,
317 GOEditor *editor,
318 G_GNUC_UNUSED GogDataAllocator *dalloc,
319 GOCmdContext *cc)
320 {
321 GtkWidget *w;
322 GogHistogramPlot *hist = GOG_HISTOGRAM_PLOT (item);
323 GtkBuilder *gui =
324 go_gtk_builder_load ("res:go:plot_distrib/gog-histogram-prefs.ui",
325 GETTEXT_PACKAGE, cc);
326 if (gui != NULL) {
327 w = go_gtk_builder_get_widget (gui, "vertical");
328 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), hist->vertical);
329 g_signal_connect (w, "toggled", G_CALLBACK (vertical_changed_cb), hist);
330
331 w = go_gtk_builder_get_widget (gui, "cumulative");
332 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w), hist->cumulative);
333 g_signal_connect (w, "toggled", G_CALLBACK (cumulative_changed_cb), hist);
334
335 w = go_gtk_builder_get_widget (gui, "before-grid");
336 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (w),
337 (GOG_PLOT (item))->rendering_order == GOG_PLOT_RENDERING_BEFORE_GRID);
338 g_signal_connect (G_OBJECT (w),
339 "toggled",
340 G_CALLBACK (display_before_grid_cb), item);
341
342 w = go_gtk_builder_get_widget (gui, "histogram-prefs");
343 go_editor_add_page (editor, w , _("Properties"));
344 g_object_unref (gui);
345 }
346
347 (GOG_OBJECT_CLASS(histogram_plot_parent_klass)->populate_editor) (item, editor, dalloc, cc);
348 }
349 #endif
350
351 static void
gog_histogram_plot_finalize(GObject * obj)352 gog_histogram_plot_finalize (GObject *obj)
353 {
354 gog_histogram_plot_clear_formats (GOG_HISTOGRAM_PLOT (obj));
355 G_OBJECT_CLASS (histogram_plot_parent_klass)->finalize (obj);
356 }
357
358 static void
gog_histogram_plot_class_init(GogPlotClass * gog_plot_klass)359 gog_histogram_plot_class_init (GogPlotClass *gog_plot_klass)
360 {
361 GObjectClass *gobject_klass = (GObjectClass *) gog_plot_klass;
362 GogObjectClass *gog_object_klass = (GogObjectClass *) gog_plot_klass;
363
364 histogram_plot_parent_klass = g_type_class_peek_parent (gog_plot_klass);
365
366 gobject_klass->finalize = gog_histogram_plot_finalize;
367 gobject_klass->get_property = gog_histogram_plot_get_property;
368 gobject_klass->set_property = gog_histogram_plot_set_property;
369
370 g_object_class_install_property (gobject_klass, HISTOGRAM_PROP_VERTICAL,
371 g_param_spec_boolean ("vertical",
372 _("Vertical"),
373 _("Draw the histogram vertically or horizontally"),
374 TRUE,
375 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
376 g_object_class_install_property (gobject_klass, HISTOGRAM_PROP_CUMULATIVE,
377 g_param_spec_boolean ("cumulative",
378 _("Cumulative"),
379 _("Use cumulated data"),
380 FALSE,
381 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
382 g_object_class_install_property (gobject_klass, HISTOGRAM_PROP_BEFORE_GRID,
383 g_param_spec_boolean ("before-grid",
384 _("Displayed under the grids"),
385 _("Should the plot be displayed before the grids"),
386 FALSE,
387 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
388
389 gog_object_klass->type_name = gog_histogram_plot_type_name;
390 gog_object_klass->view_type = gog_histogram_plot_view_get_type ();
391 gog_object_klass->update = gog_histogram_plot_update;
392 #ifdef GOFFICE_WITH_GTK
393 gog_object_klass->populate_editor = gog_histogram_plot_populate_editor;
394 #endif
395
396 {
397 static GogSeriesDimDesc dimensions[] = {
398 { N_("Limits"), GOG_SERIES_SUGGESTED, FALSE,
399 GOG_DIM_INDEX, GOG_MS_DIM_CATEGORIES },
400 { N_("Values"), GOG_SERIES_REQUIRED, FALSE,
401 GOG_DIM_VALUE, GOG_MS_DIM_VALUES },
402 };
403 gog_plot_klass->desc.series.dim = dimensions;
404 gog_plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
405 }
406 gog_plot_klass->desc.num_series_max = 1;
407 gog_plot_klass->series_type = gog_histogram_plot_series_get_type ();
408 gog_plot_klass->axis_set = GOG_AXIS_SET_XY;
409 gog_plot_klass->desc.series.style_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
410 gog_plot_klass->axis_get_bounds = gog_histogram_plot_axis_get_bounds;
411 }
412
413 static void
gog_histogram_plot_init(GogHistogramPlot * hist)414 gog_histogram_plot_init (GogHistogramPlot *hist)
415 {
416 GogPlot *plot = GOG_PLOT (hist);
417
418 plot->rendering_order = GOG_PLOT_RENDERING_BEFORE_AXIS;
419 hist->vertical = TRUE;
420 }
421
GSF_DYNAMIC_CLASS(GogHistogramPlot,gog_histogram_plot,gog_histogram_plot_class_init,gog_histogram_plot_init,GOG_TYPE_PLOT)422 GSF_DYNAMIC_CLASS (GogHistogramPlot, gog_histogram_plot,
423 gog_histogram_plot_class_init, gog_histogram_plot_init,
424 GOG_TYPE_PLOT)
425
426 /*****************************************************************************/
427 /* Double histograms */
428
429 static GObjectClass *double_histogram_plot_parent_klass;
430
431 #ifdef GOFFICE_WITH_GTK
432 static void
433 gog_double_histogram_plot_populate_editor (GogObject *gobj,
434 GOEditor *editor,
435 GogDataAllocator *dalloc,
436 GOCmdContext *cc)
437 {
438 GtkGrid *grid;
439 GtkWidget *w;
440 GogDataset *set = GOG_DATASET (gobj);
441 GtkBuilder *gui =
442 go_gtk_builder_load ("res:go:plot_distrib/gog-double-histogram-prefs.ui",
443 GETTEXT_PACKAGE, cc);
444 if (gui != NULL) {
445 grid = GTK_GRID (gtk_builder_get_object (gui, "double-histogram-prefs"));
446 w = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, 0, GOG_DATA_SCALAR));
447 gtk_widget_set_tooltip_text (w, _("Label for the first Y category. If not set or empty, \"First values\" will be used."));
448 gtk_widget_show (w);
449 gtk_widget_set_hexpand (w, TRUE);
450 gtk_grid_attach (grid, w, 1, 0, 1, 1);
451 w = GTK_WIDGET (gog_data_allocator_editor (dalloc, set, 1, GOG_DATA_SCALAR));
452 gtk_widget_set_tooltip_text (w, _("Label for the second Y category. If not set or empty, \"Second values\" will be used."));
453 gtk_widget_show (w);
454 gtk_widget_set_hexpand (w, TRUE);
455 gtk_grid_attach (grid, w, 1, 1, 1, 1);
456 go_editor_add_page (editor,
457 go_gtk_builder_get_widget (gui, "double-histogram-prefs"),
458 _("Categories labels"));
459 }
460 (GOG_OBJECT_CLASS (double_histogram_plot_parent_klass)->populate_editor) (gobj, editor, dalloc, cc);
461 }
462 #endif
463
464 static void
gog_double_histogram_plot_finalize(GObject * obj)465 gog_double_histogram_plot_finalize (GObject *obj)
466 {
467 GogDoubleHistogramPlot *plot = GOG_DOUBLE_HISTOGRAM_PLOT (obj);
468 if (plot->labels != NULL) {
469 gog_dataset_finalize (GOG_DATASET (obj));
470 g_free (plot->labels);
471 plot->labels = NULL;
472 }
473 (*double_histogram_plot_parent_klass->finalize) (obj);
474 }
475
476 static void
gog_double_histogram_plot_class_init(GogPlotClass * gog_plot_klass)477 gog_double_histogram_plot_class_init (GogPlotClass *gog_plot_klass)
478 {
479 GObjectClass *gobject_klass = (GObjectClass *) gog_plot_klass;
480 GogObjectClass *gog_klass = (GogObjectClass *) gog_plot_klass;
481 double_histogram_plot_parent_klass = g_type_class_peek_parent (gog_klass);
482 gobject_klass->finalize = gog_double_histogram_plot_finalize;
483 #ifdef GOFFICE_WITH_GTK
484 gog_klass->populate_editor = gog_double_histogram_plot_populate_editor;
485 #endif
486 {
487 static GogSeriesDimDesc dimensions[] = {
488 { N_("Limits"), GOG_SERIES_REQUIRED, FALSE,
489 GOG_DIM_INDEX, GOG_MS_DIM_CATEGORIES },
490 { N_("First values"), GOG_SERIES_REQUIRED, FALSE,
491 GOG_DIM_VALUE, GOG_MS_DIM_VALUES },
492 { N_("Second values"), GOG_SERIES_REQUIRED, FALSE,
493 GOG_DIM_VALUE, GOG_MS_DIM_EXTRA1 },
494 };
495 gog_plot_klass->desc.series.dim = dimensions;
496 gog_plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
497 gog_plot_klass->desc.series.style_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL | GO_STYLE_FONT;
498 }
499 }
500
501 static void
gog_double_histogram_plot_init(GogDoubleHistogramPlot * plot)502 gog_double_histogram_plot_init (GogDoubleHistogramPlot *plot)
503 {
504 plot->labels = g_new0 (GogDatasetElement, 2);
505 }
506
507 static void
gog_double_histogram_plot_dataset_dims(GogDataset const * set,int * first,int * last)508 gog_double_histogram_plot_dataset_dims (GogDataset const *set, int *first, int *last)
509 {
510 *first = 0;
511 *last = 1;
512 }
513
514 static GogDatasetElement *
gog_double_histogram_plot_dataset_get_elem(GogDataset const * set,int dim_i)515 gog_double_histogram_plot_dataset_get_elem (GogDataset const *set, int dim_i)
516 {
517 GogDoubleHistogramPlot const *plot = GOG_DOUBLE_HISTOGRAM_PLOT (set);
518 g_return_val_if_fail (2 > dim_i, NULL);
519 g_return_val_if_fail (dim_i >= 0, NULL);
520 return plot->labels + dim_i;
521 }
522
523 static void
gog_double_histogram_plot_dataset_dim_changed(GogDataset * set,int dim_i)524 gog_double_histogram_plot_dataset_dim_changed (GogDataset *set, int dim_i)
525 {
526 gog_object_request_update (GOG_OBJECT (set));
527 }
528
529 static void
gog_double_histogram_plot_dataset_init(GogDatasetClass * iface)530 gog_double_histogram_plot_dataset_init (GogDatasetClass *iface)
531 {
532 iface->get_elem = gog_double_histogram_plot_dataset_get_elem;
533 iface->dims = gog_double_histogram_plot_dataset_dims;
534 iface->dim_changed = gog_double_histogram_plot_dataset_dim_changed;
535 }
536
537 GSF_DYNAMIC_CLASS_FULL (GogDoubleHistogramPlot, gog_double_histogram_plot,
538 NULL, NULL, gog_double_histogram_plot_class_init, NULL,
539 gog_double_histogram_plot_init, GOG_TYPE_HISTOGRAM_PLOT, 0,
540 GSF_INTERFACE (gog_double_histogram_plot_dataset_init, GOG_TYPE_DATASET))
541
542 /*****************************************************************************/
543 typedef GogPlotView GogHistogramPlotView;
544 typedef GogPlotViewClass GogHistogramPlotViewClass;
545
546 static void
gog_histogram_plot_view_render(GogView * view,GogViewAllocation const * bbox)547 gog_histogram_plot_view_render (GogView *view, GogViewAllocation const *bbox)
548 {
549 GogHistogramPlot const *model = GOG_HISTOGRAM_PLOT (view->model);
550 GogChart *chart = GOG_CHART (view->model->parent);
551 GogChartMap *chart_map;
552 GogAxisMap *x_map, *y_map;
553 GogViewAllocation const *area;
554 GogHistogramPlotSeries const *series;
555 double *x_vals = NULL, *y_vals, y0;
556 unsigned i;
557 GOPath *path ;
558 GSList *ptr;
559 GOStyle *style;
560
561 if (model->base.series == NULL)
562 return;
563 series = GOG_HISTOGRAM_PLOT_SERIES (model->base.series->data);
564 style = GOG_STYLED_OBJECT (series)->style;
565 if (series->base.num_elements < 1)
566 return;
567 area = gog_chart_view_get_plot_area (view->parent);
568 chart_map = gog_chart_map_new (chart, area,
569 GOG_PLOT (model)->axis[GOG_AXIS_X],
570 GOG_PLOT (model)->axis[GOG_AXIS_Y],
571 NULL, FALSE);
572 if (!gog_chart_map_is_valid (chart_map)) {
573 gog_chart_map_free (chart_map);
574 return;
575 }
576
577 /* clip the plot to it's allocated rectangle, see #684195 */
578 gog_renderer_push_clip_rectangle (view->renderer, area->x, area->y, area->w, area->h);
579
580 x_map = gog_chart_map_get_axis_map (chart_map, 0);
581 y_map = gog_chart_map_get_axis_map (chart_map, 1);
582
583 if (series->real_x)
584 x_vals = series->real_x;
585 else if (series->base.values[0].data)
586 x_vals = go_data_get_values (series->base.values[0].data);
587 y_vals = series->y ? series->y : go_data_get_values (series->base.values[1].data);
588
589 path = go_path_new ();
590 go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
591 if (model->vertical) {
592 double curx = gog_axis_map_to_view (x_map, x_vals ? x_vals[0] : 0);
593 go_path_move_to (path, curx, y0 = gog_axis_map_get_baseline (y_map));
594 for (i = 0; i < series->base.num_elements; i++) {
595 double cury = gog_axis_map_to_view (y_map, y_vals[i]);
596 go_path_line_to (path, curx, cury);
597 curx = gog_axis_map_to_view (x_map, x_vals ? x_vals[i+1] : 0);
598 go_path_line_to (path, curx, cury);
599 }
600 go_path_line_to (path, curx, y0);
601 } else {
602 double curx = gog_axis_map_to_view (y_map, x_vals ? x_vals[0] : 0);
603 go_path_move_to (path, y0 = gog_axis_map_get_baseline (x_map), curx);
604 for (i = 0; i < series->base.num_elements; i++) {
605 double cury = gog_axis_map_to_view (x_map, y_vals[i]);
606 go_path_line_to (path, cury, curx);
607 curx = gog_axis_map_to_view (y_map, x_vals ? x_vals[i+1] : 0);
608 go_path_line_to (path, cury, curx);
609 }
610 go_path_line_to (path, y0, curx);
611 }
612 go_path_close (path);
613 gog_renderer_push_style (view->renderer, style);
614 gog_renderer_fill_shape (view->renderer, path);
615
616 if (series->droplines) {
617 GOPath *drop_path = go_path_new ();
618 double cury = y0;
619 go_path_set_options (drop_path, GO_PATH_OPTIONS_SHARP);
620 gog_renderer_push_style (view->renderer,
621 go_styled_object_get_style (GO_STYLED_OBJECT (series->droplines)));
622 if (model->vertical) {
623 for (i = 1; i < series->base.num_elements; i++) {
624 double x = x_vals ? x_vals[i] : 0;
625 double y = y_vals[i];
626 double y_prev = y_vals[i - 1];
627 double curx = gog_axis_map_to_view (x_map, x);
628
629 if (pos_product (y, y_prev)) {
630 go_path_move_to (drop_path, curx, y0);
631 cury = gog_axis_map_to_view
632 (y_map,
633 y > 0 ? MIN (y_prev, y) : MAX (y_prev, y));
634 } else {
635 go_path_move_to (drop_path, curx, cury);
636 cury = gog_axis_map_to_view (y_map, y);
637 }
638 go_path_line_to (drop_path, curx, cury);
639 }
640 } else {
641 for (i = 1; i < series->base.num_elements; i++) {
642 double x = x_vals ? x_vals[i] : 0;
643 double y = y_vals[i];
644 double y_prev = y_vals[i - 1];
645 double curx = gog_axis_map_to_view (y_map, x);
646
647 if (pos_product (y, y_prev)) {
648 go_path_move_to (drop_path, y0, curx);
649 cury = gog_axis_map_to_view
650 (x_map,
651 y > 0 ? MIN (y_prev, y) : MAX (y_prev, y));
652 } else {
653 go_path_move_to (drop_path, cury, curx);
654 cury = gog_axis_map_to_view (x_map, y);
655 }
656 go_path_line_to (drop_path, cury, curx);
657 }
658 }
659 gog_renderer_stroke_serie (view->renderer, drop_path);
660 go_path_free (drop_path);
661 gog_renderer_pop_style (view->renderer);
662 }
663 gog_renderer_stroke_shape (view->renderer, path);
664 gog_renderer_pop_style (view->renderer);
665 go_path_free (path);
666 /* Redo the same for the other side of a double histogram. */
667 if (GOG_IS_DOUBLE_HISTOGRAM_PLOT (model) && series->base.values[2].data != NULL) {
668 y_vals = series->y_ ? series->y_ : go_data_get_values (series->base.values[2].data);
669 path = go_path_new ();
670 go_path_set_options (path, GO_PATH_OPTIONS_SHARP);
671 if (model->vertical) {
672 double curx = gog_axis_map_to_view (x_map, x_vals ? x_vals[0]: 0);
673 go_path_move_to (path, curx, y0);
674 for (i = 0; i < series->base.num_elements; i++) {
675 double cury = gog_axis_map_to_view (y_map, y_vals[i]);
676 go_path_line_to (path, curx, cury);
677 curx = gog_axis_map_to_view (x_map, (x_vals ? x_vals[i+1]: 0.));
678 go_path_line_to (path, curx, cury);
679 }
680 go_path_line_to (path, curx, y0);
681 } else {
682 double curx = gog_axis_map_to_view (y_map, x_vals ? x_vals[0]: 0);
683 go_path_move_to (path, y0, curx);
684 for (i = 0; i < series->base.num_elements; i++) {
685 double cury = gog_axis_map_to_view (x_map, y_vals[i]);
686 go_path_line_to (path, cury, curx);
687 curx = gog_axis_map_to_view (y_map, (x_vals ? x_vals[i+1]: 0.));
688 go_path_line_to (path, cury, curx);
689 }
690 go_path_line_to (path, y0, curx);
691 }
692 go_path_close (path);
693 gog_renderer_push_style (view->renderer, style);
694 gog_renderer_fill_shape (view->renderer, path);
695
696 if (series->droplines) {
697 GOPath *drop_path = go_path_new ();
698 double cury = y0;
699 go_path_set_options (drop_path, GO_PATH_OPTIONS_SHARP);
700 gog_renderer_push_style (view->renderer,
701 go_styled_object_get_style (GO_STYLED_OBJECT (series->droplines)));
702 if (model->vertical) {
703 for (i = 1; i < series->base.num_elements; i++) {
704 double x = x_vals ? x_vals[i] : 0;
705 double y = y_vals[i];
706 double y_prev = y_vals[i - 1];
707 double curx = gog_axis_map_to_view (x_map, x);
708
709 if (pos_product (y_prev, y)) {
710 go_path_move_to (drop_path, curx, y0);
711 cury = gog_axis_map_to_view
712 (y_map,
713 y > 0 ? MIN (y_prev, y) : MAX (y_prev, y));
714 } else {
715 go_path_move_to (drop_path, curx, cury);
716 cury = gog_axis_map_to_view (y_map, y);
717 }
718 go_path_line_to (drop_path, curx, cury);
719 }
720 } else {
721 for (i = 1; i < series->base.num_elements; i++) {
722 double x = x_vals ? x_vals[i] : 0;
723 double y = y_vals[i];
724 double y_prev = y_vals[i - 1];
725 double curx = gog_axis_map_to_view (y_map, x);
726
727 if (pos_product (y_prev, y)) {
728 go_path_move_to (drop_path, y0, curx);
729 cury = gog_axis_map_to_view
730 (x_map,
731 y > 0 ? MIN (y_prev, y) : MAX (y_prev, y));
732 } else {
733 go_path_move_to (drop_path, cury, curx);
734 cury = gog_axis_map_to_view (x_map, y);
735 }
736 go_path_line_to (drop_path, cury, curx);
737 }
738 }
739 gog_renderer_stroke_serie (view->renderer, drop_path);
740 go_path_free (drop_path);
741 gog_renderer_pop_style (view->renderer);
742 }
743 gog_renderer_stroke_shape (view->renderer, path);
744 go_path_free (path);
745
746 /* Now display labels FIXME: might be optional */
747 {
748 char *text1 = NULL, *text2 = NULL;
749 char const *text;
750 GogViewAllocation alloc;
751 GogDoubleHistogramPlot *dbl = GOG_DOUBLE_HISTOGRAM_PLOT (model);
752 if (dbl->labels[0].data) {
753 text1 = go_data_get_scalar_string (dbl->labels[0].data);
754 if (text1 && !*text1) {
755 g_free (text1);
756 text1 = NULL;
757 }
758 }
759 if (dbl->labels[1].data) {
760 text2 = go_data_get_scalar_string (dbl->labels[1].data);
761 if (text2 && !*text2) {
762 g_free (text2);
763 text2 = NULL;
764 }
765 }
766 text = text1 ? text1 : _("First values");
767 if (model->cumulative) {
768 if (model->vertical) {
769 alloc.x = area->x + 2; /* FIXME: replace 2 by something configurable */
770 alloc.y = area->y + 2;
771 gog_renderer_draw_text (view->renderer, text, &alloc,
772 GO_ANCHOR_NORTH_WEST, FALSE,
773 GO_JUSTIFY_LEFT, -1.);
774 text = text2 ? text2 : _("Second values");
775 alloc.y = area->y + area->h - 2;
776 gog_renderer_draw_text (view->renderer, text, &alloc,
777 GO_ANCHOR_SOUTH_WEST, FALSE,
778 GO_JUSTIFY_LEFT, -1.);
779 } else {
780 alloc.x = area->x + area->w - 2; /* FIXME: replace 2 by something configurable */
781 alloc.y = area->y + area->h - 2;
782 gog_renderer_draw_text (view->renderer, text, &alloc,
783 GO_ANCHOR_SOUTH_EAST, FALSE,
784 GO_JUSTIFY_LEFT, -1.);
785 text = text2 ? text2 : _("Second values");
786 alloc.x = area->x + 2;
787 gog_renderer_draw_text (view->renderer, text, &alloc,
788 GO_ANCHOR_SOUTH_WEST, FALSE,
789 GO_JUSTIFY_LEFT, -1.);
790 }
791 } else {
792 alloc.x = area->x + area->w - 2; /* FIXME: replace 2 by something configurable */
793 alloc.y = area->y + 2;
794 gog_renderer_draw_text (view->renderer, text, &alloc,
795 GO_ANCHOR_NORTH_EAST, FALSE,
796 GO_JUSTIFY_LEFT, -1.);
797 text = text2 ? text2 : _("Second values");
798 if (model->vertical) {
799 alloc.y = area->y + area->h - 2;
800 gog_renderer_draw_text (view->renderer, text, &alloc,
801 GO_ANCHOR_SOUTH_EAST, FALSE,
802 GO_JUSTIFY_LEFT, -1.);
803 } else {
804 alloc.x = area->x + 2;
805 gog_renderer_draw_text (view->renderer, text, &alloc,
806 GO_ANCHOR_NORTH_WEST, FALSE,
807 GO_JUSTIFY_LEFT, -1.);
808 }
809 }
810 g_free (text1);
811 g_free (text2);
812 }
813 gog_renderer_pop_style (view->renderer);
814 }
815 gog_renderer_pop_clip (view->renderer);
816
817 /* Now render children */
818 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
819 gog_view_render (ptr->data, bbox);
820
821 gog_chart_map_free (chart_map);
822 }
823
824 static GogViewClass *histogram_plot_view_parent_klass;
825
826 static void
gog_histogram_plot_view_size_allocate(GogView * view,GogViewAllocation const * allocation)827 gog_histogram_plot_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
828 {
829 GSList *ptr;
830 for (ptr = view->children; ptr != NULL; ptr = ptr->next)
831 gog_view_size_allocate (GOG_VIEW (ptr->data), allocation);
832 (histogram_plot_view_parent_klass->size_allocate) (view, allocation);
833 }
834
835 static void
gog_histogram_plot_view_class_init(GogViewClass * view_klass)836 gog_histogram_plot_view_class_init (GogViewClass *view_klass)
837 {
838 histogram_plot_view_parent_klass = (GogViewClass*) g_type_class_peek_parent (view_klass);
839 view_klass->render = gog_histogram_plot_view_render;
840 view_klass->size_allocate = gog_histogram_plot_view_size_allocate;
841 view_klass->clip = FALSE;
842 }
843
844 GSF_DYNAMIC_CLASS (GogHistogramPlotView, gog_histogram_plot_view,
845 gog_histogram_plot_view_class_init, NULL,
846 GOG_TYPE_PLOT_VIEW)
847
848 /*****************************************************************************/
849
850 typedef GogView GogHistogramSeriesView;
851 typedef GogViewClass GogHistogramSeriesViewClass;
852
853 #define GOG_TYPE_HISTOGRAM_SERIES_VIEW (gog_histogram_series_view_get_type ())
854 #define GOG_HISTOGRAM_SERIES_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_HISTOGRAM_SERIES_VIEW, GogHistogramSeriesView))
855 #define GOG_IS_HISTOGRAM_SERIES_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_HISTOGRAM_SERIES_VIEW))
856
857 static void
gog_histogram_series_view_render(GogView * view,GogViewAllocation const * bbox)858 gog_histogram_series_view_render (GogView *view, GogViewAllocation const *bbox)
859 {
860 GSList *ptr;
861 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next)
862 gog_view_render (ptr->data, bbox);
863 }
864
865 static void
gog_histogram_series_view_size_allocate(GogView * view,GogViewAllocation const * allocation)866 gog_histogram_series_view_size_allocate (GogView *view, GogViewAllocation const *allocation)
867 {
868 GSList *ptr;
869
870 for (ptr = view->children; ptr != NULL; ptr = ptr->next)
871 gog_view_size_allocate (GOG_VIEW (ptr->data), allocation);
872 }
873
874 static void
gog_histogram_series_view_class_init(GogHistogramSeriesViewClass * gview_klass)875 gog_histogram_series_view_class_init (GogHistogramSeriesViewClass *gview_klass)
876 {
877 GogViewClass *view_klass = GOG_VIEW_CLASS (gview_klass);
878 view_klass->render = gog_histogram_series_view_render;
879 view_klass->size_allocate = gog_histogram_series_view_size_allocate;
880 view_klass->build_toolkit = NULL;
881 }
882
GSF_DYNAMIC_CLASS(GogHistogramSeriesView,gog_histogram_series_view,gog_histogram_series_view_class_init,NULL,GOG_TYPE_VIEW)883 GSF_DYNAMIC_CLASS (GogHistogramSeriesView, gog_histogram_series_view,
884 gog_histogram_series_view_class_init, NULL,
885 GOG_TYPE_VIEW)
886
887 /*****************************************************************************/
888
889 static gboolean
890 drop_lines_can_add (GogObject const *parent)
891 {
892 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (parent);
893 return (series->droplines == NULL);
894 }
895
896 static void
drop_lines_post_add(GogObject * parent,GogObject * child)897 drop_lines_post_add (GogObject *parent, GogObject *child)
898 {
899 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (parent);
900 series->droplines = child;
901 gog_object_request_update (child);
902 }
903
904 static void
drop_lines_pre_remove(GogObject * parent,GogObject * child)905 drop_lines_pre_remove (GogObject *parent, GogObject *child)
906 {
907 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (parent);
908 series->droplines = NULL;
909 }
910
911 /****************************************************************************/
912
913 static GogObjectClass *gog_histogram_plot_series_parent_klass;
914
915 static GObjectClass *series_parent_klass;
916
917 static void
gog_histogram_plot_series_update(GogObject * obj)918 gog_histogram_plot_series_update (GogObject *obj)
919 {
920 double *x_vals = NULL, *y_vals = NULL, *y__vals = NULL, cur;
921 int x_len = 1, y_len = 0, y__len = 0, max, nb = 0, i, y_0 = 0, y__0 = 0;
922 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (obj);
923 unsigned old_num = series->base.num_elements;
924 GSList *ptr;
925 gboolean y_as_raw = FALSE;
926 double width = -1., origin = 0.;
927
928 g_free (series->real_x);
929 series->real_x = NULL;
930 g_free (series->real_y);
931 series->real_y = NULL;
932 g_free (series->real_y_);
933 series->real_y_ = NULL;
934 if (series->base.values[1].data != NULL) {
935 y_vals = go_data_get_values (series->base.values[1].data);
936 nb = y_len = go_data_get_vector_size (series->base.values[1].data);
937 }
938 if (GOG_IS_DOUBLE_HISTOGRAM_PLOT (series->base.plot) && series->base.values[2].data != NULL) {
939 y__vals = go_data_get_values (series->base.values[2].data);
940 y__len = go_data_get_vector_size (series->base.values[2].data);
941 if (y__len > y_len)
942 nb = y__len;
943 }
944 if (series->base.values[0].data != NULL) {
945 x_vals = go_data_get_values (series->base.values[0].data);
946 max = go_data_get_vector_size (series->base.values[0].data);
947 if (max == 2) {
948 origin = x_vals[1];
949 max = 1;
950 }
951 if (max == 1) {
952 /* use the value as bin width */
953 width = x_vals[0];
954 if (!go_finite (width))
955 width = -1.;
956 y_as_raw = TRUE;
957 } else if (max > nb) {
958 if (max > 0 && go_finite (x_vals[0])) {
959 cur = x_vals[0];
960 for (i = 1; i < max; i++) {
961 if (go_finite (x_vals[i]) && x_vals[i] > cur) {
962 cur = x_vals[i];
963 x_len++;
964 } else
965 break;
966 }
967 }
968 } else {
969 x_len = max;
970 y_as_raw = TRUE;
971 }
972 } else
973 y_as_raw = TRUE;
974 if (y_as_raw) {
975 double *y = NULL, *y_ = NULL;
976 if (y_vals) {
977 y = go_range_sort (y_vals, y_len);
978 while (y_0 < y_len && !go_finite (y[y_0]))
979 y_0++;
980 while (y_len > y_0 && !go_finite (y[y_len-1]))
981 y_len--;
982 }
983 if (y__vals) {
984 y_ = go_range_sort (y__vals, y__len);
985 while (y__0 < y__len && !go_finite (y[y__0]))
986 y__0++;
987 while (y__len > y__0 && !go_finite (y_[y__len-1]))
988 y__len--;
989 }
990 if (!x_vals || x_len <= 1) {
991 /* guess reasonable values */
992 if (width <= 0) {
993 max = go_fake_round (sqrt (MAX (y_len, y__len)));
994 if (y && y_len - y_0 > 2)
995 width = (y[y_len-1] - y[y_0]) / max;
996 if (y_ && y__len - y__0 > 2) {
997 double w_ = (y_[y__len-1] - y_[y__0]) / max;
998 width = MAX (width, w_);
999 }
1000 if (width > 0.) {
1001 width = log10 (width);
1002 max = floor (width);
1003 width -= max;
1004 if (width < 0.15)
1005 width = pow (10, max);
1006 else if (width < 0.45)
1007 width = 2 * pow (10, max);
1008 else if (width < 0.85)
1009 width = 2 * pow (10, max);
1010 else
1011 width = pow (10, max + 1);
1012 }
1013 }
1014 if (width > 0. && (y || y_)) {
1015 double m, M, nm;
1016 /* ignore nans */
1017 if (y) {
1018 m = y_ ? (MIN (y[y_0], y_[y__0])) : y[y_0];
1019 M = y_ ? MAX (y[y_len-1], y_[y__len-1]) : y[y_len-1];
1020 } else {
1021 m = y_[y__0];
1022 M = y_[y__len-1];
1023 }
1024 if (!go_finite (m) || !go_finite (M))
1025 return;
1026 /* round m */
1027 nm = floor ((m - origin)/ width) * width + origin;
1028 /* we need to include all data so we must ensure that the
1029 * first bin must be lower than the first value, see #742996 */
1030 m = (nm == m)? nm - width: nm;
1031 x_len = ceil ((M - m) / width) + 1;
1032 series->real_x = g_new (double, x_len);
1033 for (max = 0; max < x_len; max++) {
1034 series->real_x[max] = m;
1035 m += width;
1036 }
1037 }
1038 } else
1039 series->real_x = go_range_sort (x_vals, x_len);
1040 if (x_len > 1) {
1041 series->real_y = g_new0 (double, x_len - 1);
1042 for (i = 0; i < y_len && y[i] <= series->real_x[0]; i++);
1043 for (max = 1; i < y_len; i++)
1044 if (go_finite (y[i])) {
1045 while (max < x_len - 1 && y[i] > series->real_x[max])
1046 max++;
1047 if (y[i] > series->real_x[max])
1048 break;
1049 series->real_y[max-1]++;
1050 }
1051 if (y__len > 0) {
1052 series->real_y_ = g_new0 (double, x_len - 1);
1053 for (i = 0; i < y__len && y_[i] <= series->real_x[0]; i++);
1054 for (max = 1; i < y__len; i++)
1055 if (go_finite (y_[i])) {
1056 while (y_[i] > series->real_x[max]) {
1057 max++;
1058 if (max == x_len - 1)
1059 break;
1060 }
1061 series->real_y_[max-1]++;
1062 }
1063 }
1064 }
1065 }
1066 series->base.num_elements = (x_len > 0 ? MIN (x_len - 1, nb) : 0);
1067
1068 /* update children */
1069 for (ptr = obj->children; ptr != NULL; ptr = ptr->next)
1070 if (!GOG_IS_SERIES_LINES (ptr->data))
1071 gog_object_request_update (GOG_OBJECT (ptr->data));
1072
1073 /* queue plot for redraw */
1074 gog_object_request_update (GOG_OBJECT (series->base.plot));
1075 if (old_num != series->base.num_elements)
1076 gog_plot_request_cardinality_update (series->base.plot);
1077
1078 if (gog_histogram_plot_series_parent_klass->update)
1079 gog_histogram_plot_series_parent_klass->update (obj);
1080 }
1081
1082 static void
gog_histogram_plot_series_finalize(GObject * obj)1083 gog_histogram_plot_series_finalize (GObject *obj)
1084 {
1085 GogHistogramPlotSeries *series = GOG_HISTOGRAM_PLOT_SERIES (obj);
1086
1087 g_free (series->y);
1088 series->y = NULL;
1089 g_free (series->y_);
1090 series->y_ = NULL;
1091 g_free (series->x);
1092 series->x = NULL;
1093 g_free (series->real_x);
1094 series->real_x = NULL;
1095 g_free (series->real_y);
1096 series->real_y = NULL;
1097 g_free (series->real_y_);
1098 series->real_y_ = NULL;
1099
1100 G_OBJECT_CLASS (series_parent_klass)->finalize (obj);
1101 }
1102
1103 static unsigned
gog_histogram_plot_series_get_xy_data(GogSeries const * series,double const ** x,double const ** y)1104 gog_histogram_plot_series_get_xy_data (GogSeries const *series,
1105 double const **x, double const **y)
1106 {
1107 GogHistogramPlotSeries *hist_ser = GOG_HISTOGRAM_PLOT_SERIES (series);
1108
1109 *x = hist_ser->x;
1110 *y = hist_ser->y ? hist_ser->y : go_data_get_values (series->values[1].data);
1111
1112 return series->num_elements;
1113 }
1114
1115 static void
gog_histogram_plot_series_class_init(GogObjectClass * obj_klass)1116 gog_histogram_plot_series_class_init (GogObjectClass *obj_klass)
1117 {
1118 static GogObjectRole const roles[] = {
1119 { N_("Drop lines"), "GogSeriesLines", 0,
1120 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
1121 drop_lines_can_add,
1122 NULL,
1123 NULL,
1124 drop_lines_post_add,
1125 drop_lines_pre_remove,
1126 NULL },
1127 };
1128 GogSeriesClass *series_klass = (GogSeriesClass*) obj_klass;
1129 GogObjectClass *gog_klass = (GogObjectClass *)obj_klass;
1130 GObjectClass *gobject_klass = (GObjectClass *) obj_klass;
1131
1132 series_parent_klass = g_type_class_peek_parent (obj_klass);
1133 gobject_klass->finalize = gog_histogram_plot_series_finalize;
1134
1135 gog_histogram_plot_series_parent_klass = g_type_class_peek_parent (obj_klass);
1136 obj_klass->update = gog_histogram_plot_series_update;
1137 gog_klass->view_type = gog_histogram_series_view_get_type ();
1138
1139 gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
1140
1141 series_klass->get_xy_data = gog_histogram_plot_series_get_xy_data;
1142 }
1143
1144 static void
gog_histogram_plot_series_init(GObject * obj)1145 gog_histogram_plot_series_init (GObject *obj)
1146 {
1147 GogSeries *series = GOG_SERIES (obj);
1148
1149 series->acceptable_children = GOG_SERIES_ACCEPT_TREND_LINE;
1150 }
1151
1152 GSF_DYNAMIC_CLASS (GogHistogramPlotSeries, gog_histogram_plot_series,
1153 gog_histogram_plot_series_class_init, gog_histogram_plot_series_init,
1154 GOG_TYPE_SERIES)
1155