1 /*
2 * gog-plot.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/graph/gog-plot-impl.h>
24 #include <goffice/graph/gog-plot-engine.h>
25 #include <goffice/graph/gog-series-impl.h>
26 #include <goffice/graph/gog-chart.h>
27 #include <goffice/graph/gog-axis.h>
28 #include <goffice/graph/gog-theme.h>
29 #include <goffice/graph/gog-graph.h>
30 #include <goffice/graph/gog-object-xml.h>
31 #include <goffice/graph/gog-renderer.h>
32 #include <goffice/data/go-data.h>
33 #include <goffice/math/go-math.h>
34 #include <goffice/utils/go-persist.h>
35 #include <goffice/utils/go-style.h>
36 #include <goffice/utils/go-styled-object.h>
37 #include <glib/gi18n-lib.h>
38
39 #ifdef GOFFICE_WITH_GTK
40 #include <gtk/gtk.h>
41 #endif
42
43 #include <gsf/gsf-impl-utils.h>
44 #include <string.h>
45
46 #define GOG_PLOT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GOG_TYPE_PLOT, GogPlotClass))
47
48 /**
49 * SECTION: gog-plot
50 * @short_description: A plot.
51 * @See_also: #GogChart, #GogSeries
52 *
53 * This is the object that visualizes data.
54 * To manipulate the data shown in the plot, use gog_plot_new_series() and
55 * gog_plot_clear_series()
56 *
57 * Plots are implemented as plug-ins, so make sure the plug-in system is
58 * initialized before trying to create one. See go_plugins_init()
59 *
60 * GOffice ships a number of common plot implementations by default.
61 */
62
63 /**
64 * GogPlotClass:
65 * @base: base class
66 * @desc: #GogPlotDesc
67 * @series_type: series type.
68 * @axis_set: set of use axes.
69 * @axis_get_bounds: requests the axis bounds
70 * @supports_vary_style_by_element: %TRUE if each element has its own style.
71 * @enum_in_reverse: %TRUE if the plot prefers to display series in reverse
72 * order for legends (e.g. stacked plots want top element to be the
73 * last series.
74 * @foreach_elem: enumerates the elements visible in the legend.
75 * @update_3d: updates 3D (and contour) plots.
76 * @guru_helper: customizes a new plot in the graph editor dialog.
77 * @get_percent: get the value as a percentage.
78 **/
79
80 /**
81 * GogPlotViewClass:
82 * @base: base class
83 * @get_data_at_point: returns the data index at the given position, -1 if none.
84 **/
85
86 /**
87 * GogPlotBoundInfo:
88 * @is_discrete: whether the data are discrete.
89 * @center_on_ticks: whether to center data on ticks.
90 * @fmt: #GOFormat to use.
91 * @date_conv: the used #GODateConventions.
92 *
93 * Used by plots to give formating informations to each axis.
94 * GogPlotBoundInfo::val are the values limits, GogPlotBoundInfo::logical are
95 * the actual needed display limits.
96 **/
97
98 /**
99 * GogPlotDesc:
100 * @series: series description.
101 **/
102
103 /**
104 * GogPlotRenderingOrder:
105 * @GOG_PLOT_RENDERING_LAST: render after axis and grid lines.
106 * @GOG_PLOT_RENDERING_BEFORE_AXIS: render before axis but after grid lines.
107 * @GOG_PLOT_RENDERING_BEFORE_GRID: render before grid lines.
108 **/
109
110 enum {
111 PLOT_PROP_0,
112 PLOT_PROP_VARY_STYLE_BY_ELEMENT,
113 PLOT_PROP_AXIS_X,
114 PLOT_PROP_AXIS_Y,
115 PLOT_PROP_AXIS_Z,
116 PLOT_PROP_AXIS_CIRCULAR,
117 PLOT_PROP_AXIS_RADIAL,
118 PLOT_PROP_AXIS_PSEUDO_3D,
119 PLOT_PROP_AXIS_COLOR,
120 PLOT_PROP_AXIS_BUBBLE,
121 PLOT_PROP_GROUP,
122 PLOT_PROP_DEFAULT_INTERPOLATION,
123 PLOT_PROP_GURU_HINTS
124 };
125
126 static GObjectClass *plot_parent_klass;
127
128 static gboolean gog_plot_set_axis_by_id (GogPlot *plot, GogAxisType type, unsigned id);
129 static unsigned gog_plot_get_axis_id (GogPlot const *plot, GogAxisType type);
130
131 static void
gog_plot_finalize(GObject * obj)132 gog_plot_finalize (GObject *obj)
133 {
134 GogPlot *plot = GOG_PLOT (obj);
135
136 g_slist_free (plot->series); /* GogObject does the unref */
137
138 gog_plot_axis_clear (plot, GOG_AXIS_SET_ALL); /* just in case */
139 g_free (plot->plot_group);
140 g_free (plot->guru_hints);
141
142 (*plot_parent_klass->finalize) (obj);
143 }
144
145 static gboolean
role_series_can_add(GogObject const * parent)146 role_series_can_add (GogObject const *parent)
147 {
148 GogPlot *plot = GOG_PLOT (parent);
149 return g_slist_length (plot->series) < plot->desc.num_series_max;
150 }
151
152 static GogObject *
role_series_allocate(GogObject * plot)153 role_series_allocate (GogObject *plot)
154 {
155 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
156 GType type = klass->series_type;
157
158 if (type == 0)
159 type = GOG_TYPE_SERIES;
160 return g_object_new (type, NULL);
161 }
162
163 static void
role_series_post_add(GogObject * parent,GogObject * child)164 role_series_post_add (GogObject *parent, GogObject *child)
165 {
166 GogPlot *plot = GOG_PLOT (parent);
167 GogSeries *series = GOG_SERIES (child);
168 unsigned num_dim;
169 num_dim = plot->desc.series.num_dim;
170
171 /* Alias things so that dim -1 is valid */
172 series->values = g_new0 (GogDatasetElement, num_dim+1) + 1;
173 series->plot = plot;
174 series->interpolation = plot->interpolation;
175
176 /* if there are other series associated with the plot, and there are
177 * shared dimensions, clone them over. */
178 if (series->plot->series != NULL) {
179 GogGraph *graph = gog_object_get_graph (GOG_OBJECT (plot));
180 GogSeriesDesc const *desc = &plot->desc.series;
181 GogSeries const *src = plot->series->data;
182 unsigned i;
183
184 for (i = num_dim; i-- > 0 ; ) /* name is never shared */
185 if (desc->dim[i].is_shared)
186 gog_dataset_set_dim_internal (GOG_DATASET (series),
187 i, src->values[i].data, graph);
188
189 gog_series_check_validity (series);
190 }
191
192 /* APPEND to keep order, there won't be that many */
193 plot->series = g_slist_append (plot->series, series);
194 gog_plot_request_cardinality_update (plot);
195 }
196
197 static void
role_series_pre_remove(GogObject * parent,GogObject * series)198 role_series_pre_remove (GogObject *parent, GogObject *series)
199 {
200 GogPlot *plot = GOG_PLOT (parent);
201 plot->series = g_slist_remove (plot->series, series);
202 gog_plot_request_cardinality_update (plot);
203 }
204
205 #ifdef GOFFICE_WITH_GTK
206
207 static void
cb_axis_changed(GtkComboBox * combo,GogPlot * plot)208 cb_axis_changed (GtkComboBox *combo, GogPlot *plot)
209 {
210 GtkTreeIter iter;
211 GValue value;
212 GtkTreeModel *model = gtk_combo_box_get_model (combo);
213
214 memset (&value, 0, sizeof (GValue));
215 gtk_combo_box_get_active_iter (combo, &iter);
216 gtk_tree_model_get_value (model, &iter, 1, &value);
217 gog_plot_set_axis_by_id (plot,
218 GPOINTER_TO_UINT (g_object_get_data (G_OBJECT(combo), "axis-type")),
219 g_value_get_uint (&value));
220 }
221
222 static void
gog_plot_populate_editor(GogObject * obj,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)223 gog_plot_populate_editor (GogObject *obj,
224 GOEditor *editor,
225 G_GNUC_UNUSED GogDataAllocator *dalloc,
226 GOCmdContext *cc)
227 {
228 static const char *axis_labels[GOG_AXIS_TYPES] = {
229 N_("X axis:"),
230 N_("Y axis:"),
231 N_("Z axis:"),
232 N_("Circular axis:"),
233 N_("Radial axis:"),
234 N_("Pseudo 3D axis:"),
235 N_("Color axis:"),
236 N_("Bubble axis:")
237 };
238
239 GogAxisType type;
240 GogPlot *plot = GOG_PLOT (obj);
241 unsigned count = 0, axis_count;
242 GSList *axes, *ptr;
243 GogChart *chart = GOG_CHART (gog_object_get_parent (obj));
244 GogAxis *axis;
245 GtkListStore *store;
246 GtkTreeIter iter;
247 GtkCellRenderer *cell;
248 unsigned axis_set;
249
250 g_return_if_fail (chart != NULL);
251
252 axis_set = gog_chart_get_axis_set (chart) & GOG_AXIS_SET_FUNDAMENTAL;
253 if (axis_set == GOG_AXIS_SET_XY || axis_set == GOG_AXIS_SET_RADAR) {
254 GtkWidget *combo;
255 GtkWidget *grid = gtk_grid_new ();
256
257 for (type = 0 ; type < GOG_AXIS_TYPES ; type++) {
258 if (plot->axis[type] != NULL) {
259 gtk_grid_attach (GTK_GRID (grid), gtk_label_new (_(axis_labels[type])),
260 0, count, 1, 1);
261
262 store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_UINT);
263 combo = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
264 g_object_unref (store);
265
266 cell = gtk_cell_renderer_text_new ();
267 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), cell, TRUE);
268 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), cell,
269 "text", 0,
270 NULL);
271
272 axes = gog_chart_get_axes (chart, type);
273 axis_count = 0;
274 for (ptr = axes; ptr != NULL; ptr = ptr->next) {
275 axis = GOG_AXIS (ptr->data);
276 gtk_list_store_prepend (store, &iter);
277 gtk_list_store_set (store, &iter,
278 0, gog_object_get_name (GOG_OBJECT (axis)),
279 1, gog_object_get_id (GOG_OBJECT (axis)),
280 -1);
281 if (axis == plot->axis[type])
282 gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
283 axis_count++;
284 }
285 if (axis_count < 2)
286 gtk_widget_set_sensitive (GTK_WIDGET (combo), FALSE);
287 g_slist_free (axes);
288 gtk_grid_attach (GTK_GRID (grid), combo,
289 1, count,1 ,1);
290 g_object_set_data (G_OBJECT (combo), "axis-type", GUINT_TO_POINTER (type));
291 g_signal_connect (G_OBJECT (combo), "changed",
292 G_CALLBACK (cb_axis_changed), plot);
293 count++;
294 }
295 }
296
297 if (count > 0) {
298 gtk_grid_set_column_spacing (GTK_GRID (grid), 12);
299 gtk_grid_set_row_spacing (GTK_GRID (grid), 6);
300 gtk_container_set_border_width (GTK_CONTAINER (grid), 12);
301 gtk_widget_show_all (grid);
302 go_editor_add_page (editor, grid, _("Axes"));
303 }
304 else
305 g_object_unref (grid);
306 }
307
308 (GOG_OBJECT_CLASS(plot_parent_klass)->populate_editor) (obj, editor, dalloc, cc);
309 }
310 #endif
311
312 static void
gog_plot_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)313 gog_plot_set_property (GObject *obj, guint param_id,
314 GValue const *value, GParamSpec *pspec)
315 {
316 GogPlot *plot = GOG_PLOT (obj);
317 gboolean b_tmp;
318
319 switch (param_id) {
320 case PLOT_PROP_VARY_STYLE_BY_ELEMENT:
321 b_tmp = g_value_get_boolean (value) &&
322 gog_plot_supports_vary_style_by_element (plot);
323 if (plot->vary_style_by_element ^ b_tmp) {
324 plot->vary_style_by_element = b_tmp;
325 gog_plot_request_cardinality_update (plot);
326 }
327 break;
328 case PLOT_PROP_AXIS_X:
329 gog_plot_set_axis_by_id (plot, GOG_AXIS_X, g_value_get_uint (value));
330 break;
331 case PLOT_PROP_AXIS_Y:
332 gog_plot_set_axis_by_id (plot, GOG_AXIS_Y, g_value_get_uint (value));
333 break;
334 case PLOT_PROP_AXIS_Z:
335 gog_plot_set_axis_by_id (plot, GOG_AXIS_Z, g_value_get_uint (value));
336 break;
337 case PLOT_PROP_AXIS_CIRCULAR:
338 gog_plot_set_axis_by_id (plot, GOG_AXIS_CIRCULAR, g_value_get_uint (value));
339 break;
340 case PLOT_PROP_AXIS_RADIAL:
341 gog_plot_set_axis_by_id (plot, GOG_AXIS_RADIAL, g_value_get_uint (value));
342 break;
343 case PLOT_PROP_AXIS_PSEUDO_3D:
344 gog_plot_set_axis_by_id (plot, GOG_AXIS_PSEUDO_3D, g_value_get_uint (value));
345 break;
346 case PLOT_PROP_AXIS_COLOR:
347 gog_plot_set_axis_by_id (plot, GOG_AXIS_COLOR, g_value_get_uint (value));
348 break;
349 case PLOT_PROP_AXIS_BUBBLE:
350 gog_plot_set_axis_by_id (plot, GOG_AXIS_BUBBLE, g_value_get_uint (value));
351 break;
352 case PLOT_PROP_GROUP:
353 g_free (plot->plot_group);
354 plot->plot_group = g_value_dup_string (value);
355 break;
356 case PLOT_PROP_DEFAULT_INTERPOLATION:
357 plot->interpolation = go_line_interpolation_from_str (g_value_get_string (value));
358 break;
359 case PLOT_PROP_GURU_HINTS:
360 g_free (plot->guru_hints);
361 plot->guru_hints = g_value_dup_string (value);
362 break;
363
364 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
365 return; /* NOTE : RETURN */
366 }
367 }
368
369 static void
gog_plot_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)370 gog_plot_get_property (GObject *obj, guint param_id,
371 GValue *value, GParamSpec *pspec)
372 {
373 GogPlot *plot = GOG_PLOT (obj);
374 switch (param_id) {
375 case PLOT_PROP_VARY_STYLE_BY_ELEMENT:
376 g_value_set_boolean (value,
377 plot->vary_style_by_element &&
378 gog_plot_supports_vary_style_by_element (plot));
379 break;
380 case PLOT_PROP_AXIS_X:
381 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_X));
382 break;
383 case PLOT_PROP_AXIS_Y:
384 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_Y));
385 break;
386 case PLOT_PROP_AXIS_Z:
387 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_Z));
388 break;
389 case PLOT_PROP_AXIS_CIRCULAR:
390 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_CIRCULAR));
391 break;
392 case PLOT_PROP_AXIS_RADIAL:
393 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_RADIAL));
394 break;
395 case PLOT_PROP_AXIS_PSEUDO_3D:
396 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_PSEUDO_3D));
397 break;
398 case PLOT_PROP_AXIS_COLOR:
399 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_COLOR));
400 break;
401 case PLOT_PROP_AXIS_BUBBLE:
402 g_value_set_uint (value, gog_plot_get_axis_id (plot, GOG_AXIS_BUBBLE));
403 break;
404 case PLOT_PROP_GROUP:
405 g_value_set_string (value, plot->plot_group);
406 break;
407 case PLOT_PROP_DEFAULT_INTERPOLATION:
408 g_value_set_string (value, go_line_interpolation_as_str (plot->interpolation));
409 break;
410 case PLOT_PROP_GURU_HINTS:
411 g_value_set_string (value, plot->guru_hints);
412 break;
413
414 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
415 break;
416 }
417 }
418
419 static void
gog_plot_children_reordered(GogObject * obj)420 gog_plot_children_reordered (GogObject *obj)
421 {
422 GSList *ptr, *accum = NULL;
423 GogPlot *plot = GOG_PLOT (obj);
424
425 for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
426 if (GOG_IS_SERIES (ptr->data))
427 accum = g_slist_prepend (accum, ptr->data);
428 g_slist_free (plot->series);
429 plot->series = g_slist_reverse (accum);
430
431 gog_plot_request_cardinality_update (plot);
432 }
433
434 static void
gog_plot_class_init(GogObjectClass * gog_klass)435 gog_plot_class_init (GogObjectClass *gog_klass)
436 {
437 static GogObjectRole const roles[] = {
438 { N_("Series"), "GogSeries", 0,
439 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
440 role_series_can_add, NULL,
441 role_series_allocate,
442 role_series_post_add, role_series_pre_remove, NULL },
443 };
444 GObjectClass *gobject_klass = (GObjectClass *) gog_klass;
445 GogPlotClass *plot_klass = (GogPlotClass *) gog_klass;
446
447 plot_parent_klass = g_type_class_peek_parent (gog_klass);
448 gobject_klass->finalize = gog_plot_finalize;
449 gobject_klass->set_property = gog_plot_set_property;
450 gobject_klass->get_property = gog_plot_get_property;
451 #ifdef GOFFICE_WITH_GTK
452 gog_klass->populate_editor = gog_plot_populate_editor;
453 #endif
454 plot_klass->axis_set = GOG_AXIS_SET_NONE;
455 plot_klass->guru_helper = NULL;
456
457 g_object_class_install_property (gobject_klass, PLOT_PROP_VARY_STYLE_BY_ELEMENT,
458 g_param_spec_boolean ("vary-style-by-element",
459 _("Vary style by element"),
460 _("Use a different style for each segment"),
461 FALSE,
462 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
463 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_X,
464 g_param_spec_uint ("x-axis",
465 _("X axis"),
466 _("Reference to X axis"),
467 0, G_MAXINT, 0,
468 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
469 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_Y,
470 g_param_spec_uint ("y-axis",
471 _("Y axis"),
472 _("Reference to Y axis"),
473 0, G_MAXINT, 0,
474 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
475 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_Z,
476 g_param_spec_uint ("z-axis",
477 _("Z axis"),
478 _("Reference to Z axis"),
479 0, G_MAXINT, 0,
480 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
481 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_CIRCULAR,
482 g_param_spec_uint ("circ-axis",
483 _("Circular axis"),
484 _("Reference to circular axis"),
485 0, G_MAXINT, 0,
486 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
487 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_RADIAL,
488 g_param_spec_uint ("radial-axis",
489 _("Radial axis"),
490 _("Reference to radial axis"),
491 0, G_MAXINT, 0,
492 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
493 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_PSEUDO_3D,
494 g_param_spec_uint ("pseudo-3d-axis",
495 _("Pseudo-3D axis"),
496 _("Reference to pseudo-3D axis"),
497 0, G_MAXINT, 0,
498 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
499 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_COLOR,
500 g_param_spec_uint ("color-axis",
501 _("Color axis"),
502 _("Reference to color axis"),
503 0, G_MAXINT, 0,
504 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
505 g_object_class_install_property (gobject_klass, PLOT_PROP_AXIS_BUBBLE,
506 g_param_spec_uint ("bubble-axis",
507 _("Bubble axis"),
508 _("Reference to bubble axis"),
509 0, G_MAXINT, 0,
510 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
511 g_object_class_install_property (gobject_klass, PLOT_PROP_GROUP,
512 g_param_spec_string ("plot-group",
513 _("Plot group"),
514 _("Name of plot group if any"),
515 NULL,
516 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
517 g_object_class_install_property (gobject_klass, PLOT_PROP_GURU_HINTS,
518 g_param_spec_string ("guru-hints",
519 _("Guru hints"),
520 _("Semicolon separated list of hints for automatic addition of objects in "
521 "guru dialog"),
522 NULL,
523 GSF_PARAM_STATIC | G_PARAM_READWRITE));
524 g_object_class_install_property (gobject_klass, PLOT_PROP_DEFAULT_INTERPOLATION,
525 g_param_spec_string ("interpolation",
526 _("Default interpolation"),
527 _("Default type of series line interpolation"),
528 "linear",
529 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
530
531 gog_klass->children_reordered = gog_plot_children_reordered;
532 gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
533 GOG_PLOT_CLASS (gog_klass)->update_3d = NULL;
534 }
535
536 static void
gog_plot_init(GogPlot * plot,GogPlotClass const * derived_plot_klass)537 gog_plot_init (GogPlot *plot, GogPlotClass const *derived_plot_klass)
538 {
539 /* keep a local copy so that we can over-ride things if desired */
540 plot->desc = derived_plot_klass->desc;
541 /* start as true so that we can queue an update when it changes */
542 plot->cardinality_valid = TRUE;
543 plot->rendering_order = GOG_PLOT_RENDERING_LAST;
544 plot->plot_group = NULL;
545 plot->guru_hints = NULL;
546 plot->interpolation = GO_LINE_INTERPOLATION_LINEAR;
547 }
548
GSF_CLASS_ABSTRACT(GogPlot,gog_plot,gog_plot_class_init,gog_plot_init,GOG_TYPE_OBJECT)549 GSF_CLASS_ABSTRACT (GogPlot, gog_plot,
550 gog_plot_class_init, gog_plot_init,
551 GOG_TYPE_OBJECT)
552
553 GogPlot *
554 gog_plot_new_by_type (GogPlotType const *type)
555 {
556 GogPlot *res;
557
558 g_return_val_if_fail (type != NULL, NULL);
559
560 res = gog_plot_new_by_name (type->engine);
561 if (res != NULL && type->properties != NULL)
562 g_hash_table_foreach (type->properties,
563 (GHFunc) gog_object_set_arg, res);
564 return res;
565 }
566
567 /****************************************************************
568 * convenience routines
569 **/
570
571 /**
572 * gog_plot_new_series:
573 * @plot: #GogPlot
574 *
575 * Returns: (transfer none): a new GogSeries of a type consistent with @plot.
576 **/
577 GogSeries *
gog_plot_new_series(GogPlot * plot)578 gog_plot_new_series (GogPlot *plot)
579 {
580 GogObject *res;
581
582 g_return_val_if_fail (GOG_IS_PLOT (plot), NULL);
583
584 res = gog_object_add_by_name (GOG_OBJECT (plot), "Series", NULL);
585 return res ? GOG_SERIES (res) : NULL;
586 }
587
588 /**
589 * gog_plot_description: (skip)
590 * @plot: #GogPlot
591 *
592 * Returns: (transfer none): the #GogPlotDesc for @plot.
593 **/
594 GogPlotDesc const *
gog_plot_description(GogPlot const * plot)595 gog_plot_description (GogPlot const *plot)
596 {
597 g_return_val_if_fail (GOG_IS_PLOT (plot), NULL);
598 return &plot->desc;
599 }
600
601 static GogChart *
gog_plot_get_chart(GogPlot const * plot)602 gog_plot_get_chart (GogPlot const *plot)
603 {
604 return GOG_CHART (GOG_OBJECT (plot)->parent);
605 }
606
607 /* protected */
608 void
gog_plot_request_cardinality_update(GogPlot * plot)609 gog_plot_request_cardinality_update (GogPlot *plot)
610 {
611 g_return_if_fail (GOG_IS_PLOT (plot));
612
613 if (plot->cardinality_valid) {
614 GogChart *chart = gog_plot_get_chart (plot);
615 plot->cardinality_valid = FALSE;
616 gog_object_request_update (GOG_OBJECT (plot));
617 if (chart != NULL)
618 gog_chart_request_cardinality_update (chart);
619 }
620 }
621
622 /**
623 * gog_plot_update_cardinality:
624 * @plot: #GogPlot
625 * @index_num: index offset for this plot
626 *
627 * Update cardinality and cache result. See @gog_chart_get_cardinality.
628 **/
629 void
gog_plot_update_cardinality(GogPlot * plot,int index_num)630 gog_plot_update_cardinality (GogPlot *plot, int index_num)
631 {
632 GogSeries *series;
633 GSList *ptr, *children;
634 gboolean is_valid;
635 unsigned size = 0, no_legend = 0, i, j;
636
637 g_return_if_fail (GOG_IS_PLOT (plot));
638
639 plot->cardinality_valid = TRUE;
640 plot->index_num = i = j = index_num;
641
642 for (ptr = plot->series; ptr != NULL ; ptr = ptr->next) {
643 series = GOG_SERIES (ptr->data);
644 is_valid = gog_series_is_valid (GOG_SERIES (series));
645 if (plot->vary_style_by_element) {
646 if (is_valid && size < series->num_elements)
647 size = series->num_elements;
648 gog_series_set_index (series, plot->index_num, FALSE);
649 } else {
650 gog_series_set_index (series, i++, FALSE);
651 if (!gog_series_has_legend (series))
652 no_legend++;
653 j++;
654 }
655 /* now add the trend lines if any */
656 children = GOG_OBJECT (series)->children;
657 while (children) {
658 if (GOG_IS_TREND_LINE (children->data)) {
659 j++;
660 if (!gog_trend_line_has_legend (GOG_TREND_LINE (children->data)))
661 no_legend++;
662 }
663 children = children->next;
664 }
665 }
666
667 plot->full_cardinality =
668 plot->vary_style_by_element ? size : (j - plot->index_num);
669 plot->visible_cardinality = plot->full_cardinality - no_legend;
670 }
671
672 /**
673 * gog_plot_get_cardinality:
674 * @plot: #GogPlot
675 * @full: placeholder for full cardinality
676 * @visible: placeholder for visible cardinality
677 *
678 * Return the number of logical elements in the plot.
679 * See @gog_chart_get_cardinality.
680 *
681 * @full and @visible may be %NULL.
682 **/
683 void
gog_plot_get_cardinality(GogPlot * plot,unsigned * full,unsigned * visible)684 gog_plot_get_cardinality (GogPlot *plot, unsigned *full, unsigned *visible)
685 {
686 g_return_if_fail (GOG_IS_PLOT (plot));
687
688 if (!plot->cardinality_valid)
689 g_warning ("[GogPlot::get_cardinality] Invalid cardinality");
690
691 if (full != NULL)
692 *full = plot->full_cardinality;
693 if (visible != NULL)
694 *visible = plot->visible_cardinality;
695 }
696
697 static gboolean
gog_plot_enum_in_reverse(GogPlot const * plot)698 gog_plot_enum_in_reverse (GogPlot const *plot)
699 {
700 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
701 return klass != NULL && klass->enum_in_reverse && (klass->enum_in_reverse) (plot);
702 }
703
704 /**
705 * gog_plot_foreach_elem:
706 * @plot: #GogPlot
707 * @only_visible: whether to restrict to visible elements.
708 * @handler: (scope call): #GogEnumFunc
709 * @data: user data for @func
710 *
711 * Executes @funcfor each plot element. Used to build a legend.
712 **/
713 void
gog_plot_foreach_elem(GogPlot * plot,gboolean only_visible,GogEnumFunc func,gpointer data)714 gog_plot_foreach_elem (GogPlot *plot, gboolean only_visible,
715 GogEnumFunc func, gpointer data)
716 {
717 GSList *ptr, *children;
718 GogSeries const *series;
719 GOStyle *style, *tmp_style;
720 GOData *labels;
721 unsigned i, n, num_labels = 0;
722 char *label = NULL;
723 GogTheme *theme = gog_object_get_theme (GOG_OBJECT (plot));
724 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
725 GList *overrides;
726 PangoAttrList *pl;
727
728 g_return_if_fail (GOG_IS_PLOT (plot));
729 if (!plot->cardinality_valid)
730 gog_plot_get_cardinality (plot, NULL, NULL);
731
732 if (klass->foreach_elem) {
733 klass->foreach_elem (plot, only_visible, func, data);
734 return;
735 }
736
737 ptr = plot->series;
738 if (ptr == NULL)
739 return;
740
741 if (!plot->vary_style_by_element) {
742 GSList *tmp = NULL;
743
744 unsigned i = plot->index_num;
745
746 if (gog_plot_enum_in_reverse (plot))
747 ptr = tmp = g_slist_reverse (g_slist_copy (ptr));
748
749 for (; ptr != NULL ; ptr = ptr->next) {
750 if (!only_visible || gog_series_has_legend (ptr->data)) {
751 GOData *dat = gog_dataset_get_dim (GOG_DATASET (ptr->data), -1);
752 func (i, go_styled_object_get_style (ptr->data),
753 gog_object_get_name (ptr->data),
754 (dat? go_data_get_scalar_markup (dat): NULL),
755 data);
756 i++;
757 }
758 /* now add the trend lines if any */
759 children = GOG_OBJECT (ptr->data)->children;
760 while (children) {
761 if (GOG_IS_TREND_LINE (children->data) &&
762 gog_trend_line_has_legend (GOG_TREND_LINE (children->data))) {
763 func (i, go_styled_object_get_style (children->data),
764 gog_object_get_name (children->data), NULL, data);
765 i++;
766 }
767 children = children->next;
768 }
769 }
770
771 g_slist_free (tmp);
772
773 return;
774 }
775
776 series = ptr->data; /* start with the first */
777 labels = NULL;
778 if (series->values[0].data != NULL) {
779 labels = series->values[0].data;
780 num_labels = go_data_get_vector_size (labels);
781 }
782 style = go_style_dup (series->base.style);
783 n = only_visible ? plot->visible_cardinality : plot->full_cardinality;
784 for (overrides = series->overrides, i = 0; i < n ; i++) {
785 if (overrides != NULL &&
786 (GOG_SERIES_ELEMENT (overrides->data)->index == i)) {
787 tmp_style = GOG_STYLED_OBJECT (overrides->data)->style;
788 overrides = overrides->next;
789 } else
790 tmp_style = style;
791
792 gog_theme_fillin_style (theme, tmp_style, GOG_OBJECT (series),
793 plot->index_num + i, tmp_style->interesting_fields);
794 if (labels != NULL) {
795 label = (i < num_labels) ? go_data_get_vector_string (labels, i) : g_strdup ("");
796 pl = (i < num_labels)? go_data_get_vector_markup (labels, i): NULL;
797 } else {
798 label = NULL;
799 pl = NULL;
800 }
801 if (label == NULL)
802 label = g_strdup_printf ("%d", i);
803 (func) (i, tmp_style, label, pl, data);
804 g_free (label);
805 }
806 g_object_unref (style);
807 }
808
809 /**
810 * gog_plot_get_series:
811 * @plot: #GogPlot
812 *
813 * Returns: (transfer none) (element-type GogSeries) : A list of the series in @plot. Do not modify or free the list.
814 **/
815 GSList const *
gog_plot_get_series(GogPlot const * plot)816 gog_plot_get_series (GogPlot const *plot)
817 {
818 g_return_val_if_fail (GOG_IS_PLOT (plot), NULL);
819 return plot->series;
820 }
821
822 /**
823 * gog_plot_get_axis_bounds:
824 * @plot: #GogPlot
825 * @axis: #GogAxisType
826 * @bounds: #GogPlotBoundInfo
827 *
828 * Queries @plot for its axis preferences for @axis and stores the results in
829 * @bounds. All elements of @bounds are initialized to sane values before the
830 * query _EXCEPT_ ::fmt. The caller is responsible for initializing it. This
831 * is done so that once on plot has selected a format the others need not do
832 * the lookup too if so desired.
833 *
834 * Caller is responsible for unrefing ::fmt.
835 *
836 * Returns: (transfer none): The data providing the bound (does not add a ref)
837 **/
838 GOData *
gog_plot_get_axis_bounds(GogPlot * plot,GogAxisType axis,GogPlotBoundInfo * bounds)839 gog_plot_get_axis_bounds (GogPlot *plot, GogAxisType axis,
840 GogPlotBoundInfo *bounds)
841 {
842 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
843
844 g_return_val_if_fail (klass != NULL, NULL);
845 g_return_val_if_fail (bounds != NULL, NULL);
846
847 bounds->val.minima = DBL_MAX;
848 bounds->val.maxima = -DBL_MAX;
849 bounds->logical.maxima = go_nan;
850 bounds->logical.minima = go_nan;
851 bounds->is_discrete = FALSE;
852 bounds->center_on_ticks = TRUE;
853 bounds->date_conv = NULL;
854
855 if (klass->axis_get_bounds == NULL)
856 return NULL;
857 return (klass->axis_get_bounds) (plot, axis, bounds);
858 }
859
860 gboolean
gog_plot_supports_vary_style_by_element(GogPlot const * plot)861 gog_plot_supports_vary_style_by_element (GogPlot const *plot)
862 {
863 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
864
865 g_return_val_if_fail (klass != NULL, FALSE);
866
867 if (klass->supports_vary_style_by_element)
868 return (klass->supports_vary_style_by_element) (plot);
869 return TRUE; /* default */
870 }
871
872 GogAxisSet
gog_plot_axis_set_pref(GogPlot const * plot)873 gog_plot_axis_set_pref (GogPlot const *plot)
874 {
875 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
876
877 g_return_val_if_fail (klass != NULL, GOG_AXIS_SET_UNKNOWN);
878 return klass->axis_set;
879 }
880
881 gboolean
gog_plot_axis_set_is_valid(GogPlot const * plot,GogAxisSet axis_set)882 gog_plot_axis_set_is_valid (GogPlot const *plot, GogAxisSet axis_set)
883 {
884 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
885
886 g_return_val_if_fail (klass != NULL, FALSE);
887 return (axis_set == klass->axis_set);
888 }
889
890 /**
891 * gog_plot_set_axis:
892 * @plot: #GogPlot
893 * @axis: #GogAxis
894 *
895 * Connect @axis and @plot.
896 **/
897 void
gog_plot_set_axis(GogPlot * plot,GogAxis * axis)898 gog_plot_set_axis (GogPlot *plot, GogAxis *axis)
899 {
900 GogAxisType type;
901 g_return_if_fail (GOG_IS_PLOT (plot));
902 g_return_if_fail (GOG_IS_AXIS (axis));
903
904 type = gog_axis_get_atype (axis);
905 g_return_if_fail (type != GOG_AXIS_UNKNOWN);
906
907 if (plot->axis[type] == axis)
908 return;
909
910 if (plot->axis[type] != NULL)
911 gog_axis_del_contributor (plot->axis[type], GOG_OBJECT (plot));
912 plot->axis[type] = axis;
913 gog_axis_add_contributor (axis, GOG_OBJECT (plot));
914 }
915
916 static gboolean
gog_plot_set_axis_by_id(GogPlot * plot,GogAxisType type,unsigned id)917 gog_plot_set_axis_by_id (GogPlot *plot, GogAxisType type, unsigned id)
918 {
919 GogChart const *chart;
920 GogAxis *axis;
921 GSList *axes, *ptr;
922 gboolean found = FALSE;
923
924 if (id == 0)
925 return FALSE;
926
927 g_return_val_if_fail (GOG_IS_PLOT (plot), FALSE);
928 g_return_val_if_fail (GOG_OBJECT (plot)->parent != NULL, FALSE);
929
930 chart = gog_plot_get_chart (plot);
931 g_return_val_if_fail (GOG_CHART (chart) != NULL, FALSE);
932
933 axes = gog_chart_get_axes (chart, type);
934 g_return_val_if_fail (axes != NULL, FALSE);
935
936 for (ptr = axes; ptr != NULL && !found; ptr = ptr->next) {
937 axis = GOG_AXIS (ptr->data);
938 if (gog_object_get_id (GOG_OBJECT (axis)) == id) {
939 gog_plot_set_axis (plot, axis);
940 found = TRUE;
941 }
942 }
943 g_slist_free (axes);
944 return found;
945 }
946
947 gboolean
gog_plot_axis_set_assign(GogPlot * plot,GogAxisSet axis_set)948 gog_plot_axis_set_assign (GogPlot *plot, GogAxisSet axis_set)
949 {
950 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
951 GogChart const *chart;
952 GogAxisType type;
953
954 g_return_val_if_fail (klass != NULL, FALSE);
955
956 chart = gog_plot_get_chart (plot);
957 for (type = 0 ; type < GOG_AXIS_TYPES ; type++) {
958 if (plot->axis[type] != NULL) {
959 if (!(axis_set & (1 << type))) {
960 gog_axis_del_contributor (plot->axis[type], GOG_OBJECT (plot));
961 plot->axis[type] = NULL;
962 }
963 } else if (axis_set & (1 << type)) {
964 GSList *axes = gog_chart_get_axes (chart, type);
965 if (axes != NULL) {
966 gog_axis_add_contributor (axes->data, GOG_OBJECT (plot));
967 plot->axis[type] = axes->data;
968 g_slist_free (axes);
969 }
970 }
971 }
972
973 return (axis_set == klass->axis_set);
974 }
975
976 /**
977 * gog_plot_axis_clear:
978 * @plot: #GogPlot
979 * @filter: #GogAxisSet
980 *
981 * A utility method to clear all existing axis associations flagged by @filter
982 **/
983 void
gog_plot_axis_clear(GogPlot * plot,GogAxisSet filter)984 gog_plot_axis_clear (GogPlot *plot, GogAxisSet filter)
985 {
986 GogAxisType type;
987
988 g_return_if_fail (GOG_IS_PLOT (plot));
989
990 for (type = 0 ; type < GOG_AXIS_TYPES ; type++)
991 if (plot->axis[type] != NULL && ((1 << type) & filter)) {
992 gog_axis_del_contributor (plot->axis[type], GOG_OBJECT (plot));
993 plot->axis[type] = NULL;
994 }
995 }
996
997 static unsigned
gog_plot_get_axis_id(GogPlot const * plot,GogAxisType type)998 gog_plot_get_axis_id (GogPlot const *plot, GogAxisType type)
999 {
1000 GogAxis *axis = gog_plot_get_axis (plot, type);
1001
1002 return axis != NULL ? gog_object_get_id (GOG_OBJECT (axis)) : 0;
1003 }
1004
1005 /**
1006 * gog_plot_get_axis:
1007 * @plot: #GogPlot
1008 * @type: #GogAxisType
1009 *
1010 * Returns: (transfer none): the axis if any.
1011 */
1012 GogAxis *
gog_plot_get_axis(GogPlot const * plot,GogAxisType type)1013 gog_plot_get_axis (GogPlot const *plot, GogAxisType type)
1014 {
1015 g_return_val_if_fail (GOG_IS_PLOT (plot), NULL);
1016 g_return_val_if_fail (type < GOG_AXIS_TYPES, NULL);
1017 g_return_val_if_fail (GOG_AXIS_UNKNOWN < type, NULL);
1018 return plot->axis[type];
1019 }
1020
1021 void
gog_plot_update_3d(GogPlot * plot)1022 gog_plot_update_3d (GogPlot *plot)
1023 {
1024 GogPlotClass *klass = GOG_PLOT_GET_CLASS (plot);
1025 g_return_if_fail (GOG_IS_PLOT (plot));
1026
1027 if (klass->update_3d)
1028 klass->update_3d (plot);
1029 }
1030
1031 static void
gog_plot_guru_helper_add_grid_line(GogPlot * plot,gboolean major)1032 gog_plot_guru_helper_add_grid_line (GogPlot *plot, gboolean major)
1033 {
1034 GogAxisType type;
1035
1036 for (type = 0; type < GOG_AXIS_TYPES; type++) {
1037 if ((((type == GOG_AXIS_X) |
1038 (type == GOG_AXIS_Y) |
1039 (type == GOG_AXIS_CIRCULAR) |
1040 (type == GOG_AXIS_RADIAL))) &&
1041 plot->axis[type] != NULL &&
1042 gog_axis_get_grid_line (plot->axis[type], major) == NULL)
1043 {
1044 gog_object_add_by_name (GOG_OBJECT (plot->axis[type]),
1045 major ? "MajorGrid": "MinorGrid", NULL);
1046 }
1047 }
1048 }
1049
1050 void
gog_plot_guru_helper(GogPlot * plot)1051 gog_plot_guru_helper (GogPlot *plot)
1052 {
1053 GogPlotClass *klass;
1054 char **hints;
1055 char *hint;
1056 unsigned i;
1057
1058 g_return_if_fail (GOG_IS_PLOT (plot));
1059 klass = GOG_PLOT_GET_CLASS (plot);
1060
1061 if (plot->guru_hints == NULL)
1062 return;
1063
1064 hints = g_strsplit (plot->guru_hints, ";", 0);
1065
1066 for (i = 0; i < g_strv_length (hints); i++) {
1067 hint = g_strstrip (hints[i]);
1068 if (strcmp (hints[i], "backplane") == 0) {
1069 GogChart *chart = GOG_CHART (gog_object_get_parent (GOG_OBJECT (plot)));
1070 if (chart != NULL && gog_chart_get_grid (chart) == NULL)
1071 gog_object_add_by_name (GOG_OBJECT (chart), "Backplane", NULL);
1072 } else if (strcmp (hints[i], "major-grid") == 0) {
1073 gog_plot_guru_helper_add_grid_line (plot, TRUE);
1074 } else if (strcmp (hints[i], "minor-grid") == 0) {
1075 gog_plot_guru_helper_add_grid_line (plot, FALSE);
1076 } else if (klass->guru_helper)
1077 klass->guru_helper (plot, hint);
1078 }
1079
1080 g_strfreev (hints);
1081 }
1082
gog_plot_clear_series(GogPlot * plot)1083 void gog_plot_clear_series (GogPlot *plot)
1084 {
1085 while (plot->series) {
1086 GogObject *gobj = GOG_OBJECT (plot->series->data);
1087 gog_object_clear_parent (gobj);
1088 g_object_unref (gobj);
1089 }
1090 }
1091
1092 double
gog_plot_get_percent_value(GogPlot * plot,unsigned series,unsigned index)1093 gog_plot_get_percent_value (GogPlot *plot, unsigned series, unsigned index)
1094 {
1095 GogPlotClass *klass;
1096 g_return_val_if_fail (GOG_IS_PLOT (plot), go_nan);
1097 klass = GOG_PLOT_GET_CLASS (plot);
1098 return (klass->get_percent)? klass->get_percent (plot, series, index): go_nan;
1099 }
1100
1101 /*****************************************************************************/
1102
1103 #ifdef GOFFICE_WITH_GTK
1104 typedef struct {
1105 GogViewAllocation start_position;
1106 GogViewAllocation chart_allocation;
1107 GogChart *chart;
1108 } MovePlotAreaData;
1109
1110 static gboolean
gog_tool_move_plot_area_point(GogView * view,double x,double y,GogObject ** gobj)1111 gog_tool_move_plot_area_point (GogView *view, double x, double y, GogObject **gobj)
1112 {
1113 GogViewAllocation const *plot_area = gog_chart_view_get_plot_area (view->parent);
1114
1115 return (x >= plot_area->x &&
1116 x <= (plot_area->x + plot_area->w) &&
1117 y >= plot_area->y &&
1118 y <= (plot_area->y + plot_area->h));
1119 }
1120
1121 static void
gog_tool_move_plot_area_render(GogView * view)1122 gog_tool_move_plot_area_render (GogView *view)
1123 {
1124 GogViewAllocation const *plot_area = gog_chart_view_get_plot_area (view->parent);
1125
1126 gog_renderer_draw_selection_rectangle (view->renderer, plot_area);
1127 }
1128
1129 static void
gog_tool_move_plot_area_init(GogToolAction * action)1130 gog_tool_move_plot_area_init (GogToolAction *action)
1131 {
1132 MovePlotAreaData *data = g_new0 (MovePlotAreaData, 1);
1133
1134 data->chart = GOG_CHART (action->view->parent->model);
1135 data->chart_allocation = action->view->parent->allocation;
1136 data->start_position = *gog_chart_view_get_plot_area (action->view->parent);
1137 data->start_position.x = (data->start_position.x - data->chart_allocation.x) / data->chart_allocation.w;
1138 data->start_position.w = data->start_position.w / data->chart_allocation.w;
1139 data->start_position.y = (data->start_position.y - data->chart_allocation.y) / data->chart_allocation.h;
1140 data->start_position.h = data->start_position.h / data->chart_allocation.h;
1141
1142 action->data = data;
1143 }
1144
1145 static void
gog_tool_move_plot_area_move(GogToolAction * action,double x,double y)1146 gog_tool_move_plot_area_move (GogToolAction *action, double x, double y)
1147 {
1148 GogViewAllocation plot_area;
1149 MovePlotAreaData *data = action->data;
1150
1151 plot_area.w = data->start_position.w;
1152 plot_area.h = data->start_position.h;
1153 plot_area.x = data->start_position.x + (x - action->start_x) / data->chart_allocation.w;
1154 if (plot_area.x < 0.0)
1155 plot_area.x = 0.0;
1156 else if (plot_area.x + plot_area.w > 1.0)
1157 plot_area.x = 1.0 - plot_area.w;
1158 plot_area.y = data->start_position.y + (y - action->start_y) / data->chart_allocation.h;
1159 if (plot_area.y < 0.0)
1160 plot_area.y = 0.0;
1161 else if (plot_area.y + plot_area.h > 1.0)
1162 plot_area.y = 1.0 - plot_area.h;
1163 gog_chart_set_plot_area (data->chart, &plot_area);
1164 }
1165
1166 static void
gog_tool_move_plot_area_double_click(GogToolAction * action)1167 gog_tool_move_plot_area_double_click (GogToolAction *action)
1168 {
1169 MovePlotAreaData *data = action->data;
1170
1171 gog_chart_set_plot_area (data->chart, NULL);
1172 }
1173
1174 static GogTool gog_tool_move_plot_area = {
1175 N_("Move plot area"),
1176 GDK_FLEUR,
1177 gog_tool_move_plot_area_point,
1178 gog_tool_move_plot_area_render,
1179 gog_tool_move_plot_area_init,
1180 gog_tool_move_plot_area_move,
1181 gog_tool_move_plot_area_double_click,
1182 NULL /*destroy*/
1183 };
1184
1185 static gboolean
gog_tool_resize_plot_area_point(GogView * view,double x,double y,GogObject ** gobj)1186 gog_tool_resize_plot_area_point (GogView *view, double x, double y, GogObject **gobj)
1187 {
1188 GogViewAllocation const *plot_area = gog_chart_view_get_plot_area (view->parent);
1189
1190 return gog_renderer_in_grip (x, y,
1191 plot_area->x + plot_area->w,
1192 plot_area->y + plot_area->h);
1193 }
1194
1195 static void
gog_tool_resize_plot_area_render(GogView * view)1196 gog_tool_resize_plot_area_render (GogView *view)
1197 {
1198 GogViewAllocation const *plot_area = gog_chart_view_get_plot_area (view->parent);
1199
1200 gog_renderer_draw_grip (view->renderer,
1201 plot_area->x + plot_area->w,
1202 plot_area->y + plot_area->h);
1203 }
1204
1205 static void
gog_tool_resize_plot_area_move(GogToolAction * action,double x,double y)1206 gog_tool_resize_plot_area_move (GogToolAction *action, double x, double y)
1207 {
1208 GogViewAllocation plot_area;
1209 MovePlotAreaData *data = action->data;
1210
1211 plot_area.x = data->start_position.x;
1212 plot_area.y = data->start_position.y;
1213 plot_area.w = data->start_position.w + (x - action->start_x) / data->chart_allocation.w;
1214 if (plot_area.w + plot_area.x > 1.0)
1215 plot_area.w = 1.0 - plot_area.x;
1216 else if (plot_area.w < 0.0)
1217 plot_area.w = 0.0;
1218 plot_area.h = data->start_position.h + (y - action->start_y) / data->chart_allocation.h;
1219 if (plot_area.h + plot_area.y > 1.0)
1220 plot_area.h = 1.0 - plot_area.y;
1221 else if (plot_area.h < 0.0)
1222 plot_area.h = 0.0;
1223 gog_chart_set_plot_area (data->chart, &plot_area);
1224 }
1225
1226 static GogTool gog_tool_resize_plot_area = {
1227 N_("Resize plot area"),
1228 GDK_BOTTOM_RIGHT_CORNER,
1229 gog_tool_resize_plot_area_point,
1230 gog_tool_resize_plot_area_render,
1231 gog_tool_move_plot_area_init,
1232 gog_tool_resize_plot_area_move,
1233 NULL /* double-click */,
1234 NULL /*destroy*/
1235 };
1236 #endif
1237
1238 /*****************************************************************************/
1239
1240 static GogViewClass *pview_parent_klass;
1241
1242 static void
gog_plot_view_build_toolkit(GogView * view)1243 gog_plot_view_build_toolkit (GogView *view)
1244 {
1245 #ifdef GOFFICE_WITH_GTK
1246 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_move_plot_area);
1247 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_resize_plot_area);
1248 #endif
1249 }
1250
1251 static void
gog_plot_view_class_init(GogPlotViewClass * pview_klass)1252 gog_plot_view_class_init (GogPlotViewClass *pview_klass)
1253 {
1254 GogViewClass *view_klass = (GogViewClass *) pview_klass;
1255
1256 pview_parent_klass = g_type_class_peek_parent (pview_klass);
1257
1258 view_klass->build_toolkit = gog_plot_view_build_toolkit;
1259 }
1260
GSF_CLASS_ABSTRACT(GogPlotView,gog_plot_view,gog_plot_view_class_init,NULL,GOG_TYPE_VIEW)1261 GSF_CLASS_ABSTRACT (GogPlotView, gog_plot_view,
1262 gog_plot_view_class_init, NULL,
1263 GOG_TYPE_VIEW)
1264
1265
1266 /**
1267 * gog_plot_view_get_data_at_point:
1268 * @view: #GogPlotView
1269 * @x: x position
1270 * @y: y position
1271 * @series: where to store the series
1272 *
1273 * Search a data represented at (@x,@y) in @view and set @series on success.
1274 *
1275 * return value: index of the found data in @series or -1.
1276 **/
1277 int
1278 gog_plot_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries **series)
1279 {
1280 GogPlotViewClass *klass = GOG_PLOT_VIEW_GET_CLASS (view);
1281 g_return_val_if_fail (klass != NULL, -1);
1282 return (klass->get_data_at_point)? klass->get_data_at_point (view, x, y, series): -1;
1283 }
1284