1 /*
2 * gog-chart.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-chart-impl.h>
24 #include <goffice/graph/gog-chart-map.h>
25 #include <goffice/graph/gog-chart-map-3d.h>
26 #include <goffice/graph/gog-3d-box.h>
27 #include <goffice/graph/gog-plot-impl.h>
28 #include <goffice/graph/gog-graph-impl.h>
29 #include <goffice/utils/go-style.h>
30 #include <goffice/graph/gog-view.h>
31 #include <goffice/graph/gog-axis.h>
32 #include <goffice/graph/gog-axis-line-impl.h>
33 #include <goffice/graph/gog-grid.h>
34 #include <goffice/graph/gog-grid-line.h>
35 #include <goffice/graph/gog-renderer.h>
36 #include <goffice/math/go-math.h>
37 #include <goffice/math/go-matrix3x3.h>
38 #include <goffice/utils/go-persist.h>
39
40 #include <gsf/gsf-impl-utils.h>
41 #include <glib/gi18n-lib.h>
42 #include <string.h>
43 #include <math.h>
44
45 #ifdef GOFFICE_WITH_LASEM
46 #include <goffice/graph/gog-equation.h>
47 #endif
48
49 #ifdef GOFFICE_WITH_GTK
50 #include <goffice/gtk/goffice-gtk.h>
51 #endif
52
53 /**
54 * SECTION: gog-chart
55 * @short_description: A chart.
56 * @See_also: #GogPlot
57 *
58 * #GogChart wraps one or more #GogPlot objects, so that you can superimpose
59 * them on top of each other. In addition, the chart can have a title and a legend.
60 */
61
62 static const struct {
63 char const *name;
64 GogAxisSet const axis_set;
65 } axis_set_desc[] = {
66 { "none", GOG_AXIS_SET_NONE},
67 { "x", GOG_AXIS_SET_X},
68 { "xy", GOG_AXIS_SET_XY},
69 { "xyz", GOG_AXIS_SET_XYZ},
70 { "radar", GOG_AXIS_SET_RADAR},
71 { "pseudo-3d", GOG_AXIS_SET_XY_pseudo_3d},
72 { "xy-color", GOG_AXIS_SET_XY_COLOR}
73 };
74
75 GogAxisSet
gog_axis_set_from_str(char const * str)76 gog_axis_set_from_str (char const *str)
77 {
78 GogAxisSet axis_set = GOG_AXIS_SET_NONE;
79 unsigned i;
80 gboolean found = FALSE;
81
82 if (str == NULL)
83 return GOG_AXIS_SET_NONE;
84
85 for (i = 0; i < G_N_ELEMENTS (axis_set_desc); i++)
86 if (strcmp (axis_set_desc[i].name, str) == 0) {
87 axis_set = axis_set_desc[i].axis_set;
88 found = TRUE;
89 break;
90 }
91 if (!found)
92 g_warning ("[GogAxisSet::from_str] unknown axis set (%s)", str);
93 return axis_set;
94 }
95
96 /*****************************************************************************/
97 enum {
98 CHART_PROP_0,
99 CHART_PROP_CARDINALITY_VALID,
100 CHART_PROP_PLOT_AREA,
101 CHART_PROP_PLOT_AREA_IS_MANUAL,
102 CHART_PROP_X_POS,
103 CHART_PROP_Y_POS,
104 CHART_PROP_ROWS,
105 CHART_PROP_COLUMNS
106 };
107
108 static GType gog_chart_view_get_type (void);
109 static GObjectClass *chart_parent_klass;
110
111 static void
gog_chart_update(GogObject * obj)112 gog_chart_update (GogObject *obj)
113 {
114 GogChart *chart = GOG_CHART (obj);
115 unsigned full = chart->full_cardinality;
116 unsigned visible = chart->visible_cardinality;
117
118 gog_chart_get_cardinality (chart, NULL, NULL);
119
120 if (full != chart->full_cardinality ||
121 visible != chart->visible_cardinality)
122 g_object_notify (G_OBJECT (chart), "cardinality-valid");
123 }
124
125 static void
gog_chart_finalize(GObject * obj)126 gog_chart_finalize (GObject *obj)
127 {
128 GogChart *chart = GOG_CHART (obj);
129
130 /* on exit the role remove routines are not called */
131 g_slist_free (chart->plots);
132 g_slist_free (chart->axes);
133
134 (chart_parent_klass->finalize) (obj);
135 }
136
137 static void
gog_chart_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)138 gog_chart_set_property (GObject *obj, guint param_id,
139 GValue const *value, GParamSpec *pspec)
140 {
141 GogChart *chart = GOG_CHART (obj);
142 char **str_doubles;
143 char const *str;
144 gboolean changed = FALSE;
145
146 switch (param_id) {
147 case CHART_PROP_PLOT_AREA:
148 str = g_value_get_string (value);
149 str_doubles = g_strsplit (str, " ", 4);
150 if (g_strv_length (str_doubles) != 4) {
151 g_strfreev (str_doubles);
152 break;
153 }
154 chart->plot_area.x = g_ascii_strtod (str_doubles[0], NULL);
155 chart->plot_area.y = g_ascii_strtod (str_doubles[1], NULL);
156 chart->plot_area.w = g_ascii_strtod (str_doubles[2], NULL);
157 chart->plot_area.h = g_ascii_strtod (str_doubles[3], NULL);
158 g_strfreev (str_doubles);
159 break;
160 case CHART_PROP_PLOT_AREA_IS_MANUAL:
161 chart->is_plot_area_manual = g_value_get_boolean (value);
162 break;
163 case CHART_PROP_X_POS:
164 chart->x_pos = g_value_get_int (value);
165 changed = TRUE;
166 break;
167 case CHART_PROP_Y_POS:
168 chart->y_pos = g_value_get_int (value);
169 changed = TRUE;
170 break;
171 case CHART_PROP_COLUMNS:
172 chart->cols = g_value_get_int (value);
173 changed = TRUE;
174 break;
175 case CHART_PROP_ROWS:
176 chart->rows = g_value_get_int (value);
177 changed = TRUE;
178 break;
179 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
180 return; /* NOTE : RETURN */
181 }
182
183 if (changed) {
184 gog_graph_validate_chart_layout (GOG_GRAPH (GOG_OBJECT (chart)->parent));
185 gog_object_emit_changed (GOG_OBJECT (obj), TRUE);
186 }
187 }
188
189 static void
gog_chart_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)190 gog_chart_get_property (GObject *obj, guint param_id,
191 GValue *value, GParamSpec *pspec)
192 {
193 GogChart *chart = GOG_CHART (obj);
194 GString *string;
195 char buffer[G_ASCII_DTOSTR_BUF_SIZE];
196
197 switch (param_id) {
198 case CHART_PROP_CARDINALITY_VALID:
199 g_value_set_boolean (value, chart->cardinality_valid);
200 break;
201 case CHART_PROP_PLOT_AREA:
202 string = g_string_new ("");
203 g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), chart->plot_area.x));
204 g_string_append_c (string, ' ');
205 g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), chart->plot_area.y));
206 g_string_append_c (string, ' ');
207 g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), chart->plot_area.w));
208 g_string_append_c (string, ' ');
209 g_string_append (string, g_ascii_dtostr (buffer, sizeof (buffer), chart->plot_area.h));
210 g_value_set_string (value, string->str);
211 g_string_free (string, TRUE);
212 break;
213 case CHART_PROP_PLOT_AREA_IS_MANUAL:
214 g_value_set_boolean (value, chart->is_plot_area_manual);
215 break;
216 case CHART_PROP_X_POS:
217 g_value_set_int (value, chart->x_pos);
218 break;
219 case CHART_PROP_Y_POS:
220 g_value_set_int (value, chart->y_pos);
221 break;
222 case CHART_PROP_COLUMNS:
223 g_value_set_int (value, chart->cols);
224 break;
225 case CHART_PROP_ROWS:
226 g_value_set_int (value, chart->rows);
227 break;
228
229 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
230 break;
231 }
232 }
233
234 #ifdef GOFFICE_WITH_GTK
235 typedef struct {
236 GtkBuilder *gui;
237 GtkWidget *x_spin, *y_spin, *w_spin, *h_spin;
238 gulong w_spin_signal, h_spin_signal;
239 GtkWidget *position_select_combo;
240 GtkWidget *manual_setting_grid;
241 GogChart *chart;
242 } PlotAreaPrefState;
243
244 static void
plot_area_pref_state_free(PlotAreaPrefState * state)245 plot_area_pref_state_free (PlotAreaPrefState *state)
246 {
247 g_object_unref (state->chart);
248 g_object_unref (state->gui);
249 }
250
251 static void
cb_plot_area_changed(GtkWidget * spin,PlotAreaPrefState * state)252 cb_plot_area_changed (GtkWidget *spin, PlotAreaPrefState *state)
253 {
254 GogViewAllocation pos;
255 double value;
256 double max;
257
258 value = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin)) / 100.0;
259
260 gog_chart_get_plot_area (state->chart, &pos);
261 if (spin == state->x_spin) {
262 pos.x = value;
263 max = 1.0 - pos.x;
264 g_signal_handler_block (state->w_spin, state->w_spin_signal);
265 gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->w_spin), 0.0, max * 100.0);
266 if (pos.w > max) pos.w = max;
267 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->w_spin), pos.w * 100.0);
268 g_signal_handler_unblock (state->w_spin, state->w_spin_signal);
269 }
270 else if (spin == state->y_spin) {
271 pos.y = value;
272 max = 1.0 - pos.y;
273 g_signal_handler_block (state->h_spin, state->h_spin_signal);
274 gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->h_spin), 0.0, max * 100.0);
275 if (pos.h > max) pos.h = max;
276 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->h_spin), pos.w * 100.0);
277 g_signal_handler_unblock (state->h_spin, state->h_spin_signal);
278 }
279 else if (spin == state->w_spin) {
280 pos.w = value;
281 }
282 else if (spin == state->h_spin) {
283 pos.h = value;
284 }
285 gog_chart_set_plot_area (state->chart, &pos);
286 gtk_combo_box_set_active (GTK_COMBO_BOX (state->position_select_combo), 1);
287 gtk_widget_show (state->manual_setting_grid);
288 }
289
290 static void
cb_manual_position_changed(GtkComboBox * combo,PlotAreaPrefState * state)291 cb_manual_position_changed (GtkComboBox *combo, PlotAreaPrefState *state)
292 {
293 if (gtk_combo_box_get_active (combo) == 1) {
294 GogViewAllocation plot_area;
295
296 gog_chart_get_plot_area (state->chart, &plot_area);
297 gog_chart_set_plot_area (state->chart, &plot_area);
298 gtk_widget_show (state->manual_setting_grid);
299 } else {
300 gog_chart_set_plot_area (state->chart, NULL);
301 gtk_widget_hide (state->manual_setting_grid);
302 }
303 }
304
305 static void
gog_chart_populate_editor(GogObject * gobj,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)306 gog_chart_populate_editor (GogObject *gobj,
307 GOEditor *editor,
308 G_GNUC_UNUSED GogDataAllocator *dalloc,
309 GOCmdContext *cc)
310 {
311 static guint chart_pref_page = 0;
312
313 GtkBuilder *gui;
314 PlotAreaPrefState *state;
315 gboolean is_plot_area_manual;
316 GogViewAllocation plot_area;
317 GtkWidget *w;
318 GogChart *chart = GOG_CHART (gobj);
319
320 g_return_if_fail (chart != NULL);
321
322 gui = go_gtk_builder_load_internal ("res:go:graph/gog-plot-prefs.ui", GETTEXT_PACKAGE, cc);
323 g_return_if_fail (gui != NULL);
324
325 (GOG_OBJECT_CLASS(chart_parent_klass)->populate_editor) (gobj, editor, dalloc, cc);
326
327 state = g_new (PlotAreaPrefState, 1);
328 state->chart = chart;
329 state->gui = gui;
330
331 g_object_ref (chart);
332 is_plot_area_manual = gog_chart_get_plot_area (chart, &plot_area);
333
334 state->x_spin = go_gtk_builder_get_widget (gui, "x_spin");
335 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->x_spin),
336 plot_area.x * 100.0);
337 g_signal_connect (G_OBJECT (state->x_spin), "value-changed",
338 G_CALLBACK (cb_plot_area_changed), state);
339
340 state->y_spin = go_gtk_builder_get_widget (gui, "y_spin");
341 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->y_spin),
342 plot_area.y * 100.0);
343 g_signal_connect (G_OBJECT (state->y_spin), "value-changed",
344 G_CALLBACK (cb_plot_area_changed), state);
345
346 state->w_spin = go_gtk_builder_get_widget (gui, "w_spin");
347 gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->w_spin),
348 0.0, (1.0 - plot_area.x) * 100.0);
349 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->w_spin),
350 100.0 * plot_area.w);
351 state->w_spin_signal = g_signal_connect (G_OBJECT (state->w_spin), "value-changed",
352 G_CALLBACK (cb_plot_area_changed), state);
353
354 state->h_spin = go_gtk_builder_get_widget (gui, "h_spin");
355 gtk_spin_button_set_range (GTK_SPIN_BUTTON (state->h_spin),
356 0.0, (1.0 - plot_area.y) * 100.0);
357 gtk_spin_button_set_value (GTK_SPIN_BUTTON (state->h_spin),
358 100.0 * plot_area.h);
359 state->h_spin_signal = g_signal_connect (G_OBJECT (state->h_spin), "value-changed",
360 G_CALLBACK (cb_plot_area_changed), state);
361
362 state->position_select_combo = go_gtk_builder_get_widget (gui, "position_select_combo");
363 gtk_combo_box_set_active (GTK_COMBO_BOX (state->position_select_combo),
364 is_plot_area_manual ? 1 : 0);
365 state->manual_setting_grid = go_gtk_builder_get_widget (gui, "manual-setting-grid");
366 if (!is_plot_area_manual)
367 gtk_widget_hide (state->manual_setting_grid);
368
369 g_signal_connect (G_OBJECT (state->position_select_combo),
370 "changed", G_CALLBACK (cb_manual_position_changed), state);
371
372 w = go_gtk_builder_get_widget (gui, "gog-plot-prefs");
373 g_signal_connect_swapped (G_OBJECT (w), "destroy", G_CALLBACK (plot_area_pref_state_free), state);
374 go_editor_add_page (editor, w, _("Plot area"));
375
376 go_editor_set_store_page (editor, &chart_pref_page);
377 }
378 #endif
379
380 static void
gog_chart_children_reordered(GogObject * obj)381 gog_chart_children_reordered (GogObject *obj)
382 {
383 GSList *ptr, *accum = NULL;
384 GogChart *chart = GOG_CHART (obj);
385
386 for (ptr = obj->children; ptr != NULL ; ptr = ptr->next)
387 if (GOG_IS_PLOT (ptr->data))
388 accum = g_slist_prepend (accum, ptr->data);
389 g_slist_free (chart->plots);
390 chart->plots = g_slist_reverse (accum);
391
392 gog_chart_request_cardinality_update (chart);
393 }
394
395 static gboolean
role_plot_can_add(GogObject const * parent)396 role_plot_can_add (GogObject const *parent)
397 {
398 GogChart *chart = GOG_CHART (parent);
399 return (chart->axis_set & (1 << GOG_AXIS_Z)) == 0 || g_slist_length (chart->plots) == 0;
400 }
401
402 static void
role_plot_post_add(GogObject * parent,GogObject * plot)403 role_plot_post_add (GogObject *parent, GogObject *plot)
404 {
405 GogChart *chart = GOG_CHART (parent);
406 gboolean ok = TRUE;
407 GogPlotClass *plot_klass = GOG_PLOT_CLASS (G_OBJECT_GET_CLASS (plot));
408 GogAxisSet axis_set = plot_klass->axis_set & ~GOG_AXIS_SET_FUNDAMENTAL;
409
410 if (axis_set) {
411 int i = GOG_AXIS_VIRTUAL, j = 1 << GOG_AXIS_VIRTUAL;
412 for (; i < GOG_AXIS_TYPES; i++, j <<= 1)
413 if ((axis_set & j) != 0 && (chart->axis_set & j) == 0) {
414 GogObject *axis = GOG_OBJECT (g_object_new (GOG_TYPE_AXIS, "type", i, NULL));
415 chart->axis_set |= j;
416 switch (i) {
417 case GOG_AXIS_PSEUDO_3D:
418 gog_object_add_by_name (GOG_OBJECT (chart), "Pseudo-3D-Axis", axis);
419 break;
420 case GOG_AXIS_COLOR:
421 gog_object_add_by_name (GOG_OBJECT (chart), "Color-Axis", axis);
422 break;
423 case GOG_AXIS_BUBBLE:
424 gog_object_add_by_name (GOG_OBJECT (chart), "Bubble-Axis", axis);
425 break;
426 default:
427 g_warning ("Unknown axis type: %x\n", i);
428 }
429 }
430 }
431 /* APPEND to keep order, there won't be that many */
432 chart->plots = g_slist_append (chart->plots, plot);
433 gog_chart_request_cardinality_update (chart);
434
435 if (chart->plots->next == NULL)
436 ok = gog_chart_axis_set_assign (chart,
437 gog_plot_axis_set_pref (GOG_PLOT (plot)));
438 ok |= gog_plot_axis_set_assign (GOG_PLOT (plot),
439 chart->axis_set);
440
441 /* a quick post condition to keep us on our toes */
442 g_return_if_fail (ok);
443 }
444
445 static void
role_plot_pre_remove(GogObject * parent,GogObject * plot)446 role_plot_pre_remove (GogObject *parent, GogObject *plot)
447 {
448 GogChart *chart = GOG_CHART (parent);
449 gog_plot_axis_clear (GOG_PLOT (plot), GOG_AXIS_SET_ALL);
450 chart->plots = g_slist_remove (chart->plots, plot);
451 gog_chart_request_cardinality_update (chart);
452
453 if (chart->plots == NULL)
454 gog_chart_axis_set_assign (chart, GOG_AXIS_SET_UNKNOWN);
455
456 if (chart->grid != NULL &&
457 chart->axis_set != GOG_AXIS_SET_XY &&
458 chart->axis_set != GOG_AXIS_SET_X &&
459 chart->axis_set != GOG_AXIS_SET_XY_pseudo_3d &&
460 chart->axis_set != GOG_AXIS_SET_XY_COLOR &&
461 chart->axis_set != GOG_AXIS_SET_RADAR) {
462 GogObject *grid = chart->grid; /* clear_parent clears ::grid */
463 gog_object_clear_parent (GOG_OBJECT (grid));
464 g_object_unref (grid);
465 }
466 }
467
468 static gboolean
role_grid_can_add(GogObject const * parent)469 role_grid_can_add (GogObject const *parent)
470 {
471 GogChart const *chart = GOG_CHART (parent);
472 return chart->grid == NULL &&
473 (chart->axis_set == GOG_AXIS_SET_XY ||
474 chart->axis_set == GOG_AXIS_SET_X ||
475 chart->axis_set == GOG_AXIS_SET_XY_pseudo_3d ||
476 chart->axis_set == GOG_AXIS_SET_XY_COLOR ||
477 chart->axis_set == GOG_AXIS_SET_RADAR);
478 }
479
480 static void
role_grid_post_add(GogObject * parent,GogObject * child)481 role_grid_post_add (GogObject *parent, GogObject *child)
482 {
483 GogChart *chart = GOG_CHART (parent);
484 g_return_if_fail (chart->grid == NULL);
485 chart->grid = child;
486 }
487
488 static void
role_grid_pre_remove(GogObject * parent,GogObject * grid)489 role_grid_pre_remove (GogObject *parent, GogObject *grid)
490 {
491 GogChart *chart = GOG_CHART (parent);
492 g_return_if_fail (chart->grid == grid);
493 chart->grid = NULL;
494 }
495
496 static gboolean
xy_grid_3d_can_add(GogObject const * parent)497 xy_grid_3d_can_add (GogObject const *parent)
498 {
499 return (GOG_CHART (parent)->axis_set == GOG_AXIS_SET_XYZ &&
500 NULL == gog_object_get_child_by_name (parent, "XY-Backplane"));
501 }
502
503 static gboolean
yz_grid_3d_can_add(GogObject const * parent)504 yz_grid_3d_can_add (GogObject const *parent)
505 {
506 return (GOG_CHART (parent)->axis_set == GOG_AXIS_SET_XYZ &&
507 NULL == gog_object_get_child_by_name (parent, "YZ-Backplane"));
508 }
509
510 static gboolean
zx_grid_3d_can_add(GogObject const * parent)511 zx_grid_3d_can_add (GogObject const *parent)
512 {
513 return (GOG_CHART (parent)->axis_set == GOG_AXIS_SET_XYZ &&
514 NULL == gog_object_get_child_by_name (parent, "ZX-Backplane"));
515 }
516
517 static void
grid_3d_post_add(GogObject * child,GogGridType t)518 grid_3d_post_add (GogObject *child, GogGridType t)
519 {
520 g_object_set (G_OBJECT (child), "type", (int)t, NULL);
521 }
522
xy_grid_3d_post_add(GogObject * parent,GogObject * child)523 static void xy_grid_3d_post_add (GogObject *parent, GogObject *child)
524 { grid_3d_post_add (child, GOG_GRID_XY); }
yz_grid_3d_post_add(GogObject * parent,GogObject * child)525 static void yz_grid_3d_post_add (GogObject *parent, GogObject *child)
526 { grid_3d_post_add (child, GOG_GRID_YZ); }
zx_grid_3d_post_add(GogObject * parent,GogObject * child)527 static void zx_grid_3d_post_add (GogObject *parent, GogObject *child)
528 { grid_3d_post_add (child, GOG_GRID_ZX); }
529
530 static gboolean
axis_can_add(GogObject const * parent,GogAxisType t)531 axis_can_add (GogObject const *parent, GogAxisType t)
532 {
533 GogChart *chart = GOG_CHART (parent);
534 if (chart->axis_set == GOG_AXIS_SET_UNKNOWN
535 || chart->axis_set == GOG_AXIS_SET_XYZ)
536 return FALSE;
537 return (chart->axis_set & (1 << t)) != 0;
538 }
539
540 static gboolean
axis_can_remove(GogObject const * child)541 axis_can_remove (GogObject const *child)
542 {
543 return NULL == gog_axis_contributors (GOG_AXIS (child));
544 }
545
546 static void
axis_post_add(GogObject * axis,GogAxisType t)547 axis_post_add (GogObject *axis, GogAxisType t)
548 {
549 GogChart *chart = GOG_CHART (axis->parent);
550 g_object_set (G_OBJECT (axis), "type", (int)t, NULL);
551 chart->axes = g_slist_prepend (chart->axes, axis);
552
553 gog_axis_base_set_position (GOG_AXIS_BASE (axis), GOG_AXIS_AUTO);
554 }
555
556 static void
axis_pre_remove(GogObject * parent,GogObject * child)557 axis_pre_remove (GogObject *parent, GogObject *child)
558 {
559 GogChart *chart = GOG_CHART (parent);
560 GogAxis *axis = GOG_AXIS (child);
561 GogColorScale *scale = gog_axis_get_color_scale (axis);
562 if (scale)
563 gog_color_scale_set_axis (scale, NULL);
564 gog_axis_clear_contributors (GOG_AXIS (axis));
565 chart->axes = g_slist_remove (chart->axes, axis);
566 }
567
568 /* Color scale related code */
569 static gboolean
color_scale_can_add(GogObject const * parent)570 color_scale_can_add (GogObject const *parent)
571 {
572 /* TRUE if there are more color or pseudo-3d axes than there are color scales */
573 GogChart *chart = GOG_CHART (parent);
574 GSList *ptr;
575 GogAxis *axis;
576 GogAxisType type;
577
578 if ((chart->axis_set & ((1 << GOG_AXIS_COLOR) | (1 << GOG_AXIS_PSEUDO_3D ))) == 0)
579 return FALSE;
580 for (ptr = chart->axes; ptr && ptr->data; ptr = ptr->next) {
581 axis = GOG_AXIS (ptr->data);
582 type = gog_axis_get_atype (axis);
583 if ((type == GOG_AXIS_COLOR || type == GOG_AXIS_PSEUDO_3D)
584 && gog_axis_get_color_scale (axis) == NULL)
585 return TRUE;
586 }
587 return FALSE;
588 }
589
590 static void
color_scale_post_add(GogObject * parent,GogObject * child)591 color_scale_post_add (GogObject *parent, GogObject *child)
592 {
593 /* Link the color scale to an axis without a preexisting color scale */
594 GogChart *chart = GOG_CHART (parent);
595 GSList *ptr;
596 GogAxis *axis;
597 GogAxisType type;
598 GSList const *l;
599
600 for (ptr = chart->axes; ptr && ptr->data; ptr = ptr->next) {
601 axis = GOG_AXIS (ptr->data);
602 type = gog_axis_get_atype (axis);
603 if ((type == GOG_AXIS_COLOR || type == GOG_AXIS_PSEUDO_3D)
604 && gog_axis_get_color_scale (axis) == NULL) {
605 gog_color_scale_set_axis (GOG_COLOR_SCALE (child), axis);
606 for (l = gog_axis_contributors (axis); l; l = l->next)
607 gog_object_request_update (GOG_OBJECT (l->data));
608 break;
609 }
610 }
611 gog_chart_request_cardinality_update (chart);
612 }
613
614 static void
color_scale_pre_remove(GogObject * parent,GogObject * scale)615 color_scale_pre_remove (GogObject *parent, GogObject *scale)
616 {
617 /* Unlink the color scale */
618 GSList const *l = gog_axis_contributors (gog_color_scale_get_axis (GOG_COLOR_SCALE (scale)));
619 gog_color_scale_set_axis (GOG_COLOR_SCALE (scale), NULL);
620 for (; l; l = l->next)
621 gog_object_request_update (GOG_OBJECT (l->data));
622 gog_chart_request_cardinality_update (GOG_CHART (parent));
623 }
624
625
x_axis_can_add(GogObject const * parent)626 static gboolean x_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_X); }
x_axis_post_add(GogObject * parent,GogObject * child)627 static void x_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_X); }
y_axis_can_add(GogObject const * parent)628 static gboolean y_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Y); }
y_axis_post_add(GogObject * parent,GogObject * child)629 static void y_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Y); }
z_axis_can_add(GogObject const * parent)630 static gboolean z_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_Z); }
z_axis_post_add(GogObject * parent,GogObject * child)631 static void z_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_Z); }
circular_axis_can_add(GogObject const * parent)632 static gboolean circular_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_CIRCULAR); }
circular_axis_post_add(GogObject * parent,GogObject * child)633 static void circular_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_CIRCULAR); }
radial_axis_can_add(GogObject const * parent)634 static gboolean radial_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_RADIAL); }
radial_axis_post_add(GogObject * parent,GogObject * child)635 static void radial_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_RADIAL); }
pseudo_3d_axis_can_add(GogObject const * parent)636 static gboolean pseudo_3d_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_PSEUDO_3D); }
pseudo_3d_axis_post_add(GogObject * parent,GogObject * child)637 static void pseudo_3d_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_PSEUDO_3D); }
bubble_axis_can_add(GogObject const * parent)638 static gboolean bubble_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_BUBBLE); }
bubble_axis_post_add(GogObject * parent,GogObject * child)639 static void bubble_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_BUBBLE); }
color_axis_can_add(GogObject const * parent)640 static gboolean color_axis_can_add (GogObject const *parent) { return axis_can_add (parent, GOG_AXIS_COLOR); }
color_axis_post_add(GogObject * parent,GogObject * child)641 static void color_axis_post_add (GogObject *parent, GogObject *child) { axis_post_add (child, GOG_AXIS_COLOR); }
role_3d_box_can_add(GogObject const * parent)642 static gboolean role_3d_box_can_add (GogObject const *parent) {return FALSE;}
role_3d_box_can_remove(GogObject const * parent)643 static gboolean role_3d_box_can_remove (GogObject const *parent) {return FALSE;}
644
645 static GogObjectRole const roles[] = {
646 { N_("Backplane"), "GogGrid", 1,
647 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
648 role_grid_can_add, NULL, NULL, role_grid_post_add, role_grid_pre_remove, NULL, { -1 } },
649 { N_("XY-Backplane"), "GogGrid", 1,
650 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
651 xy_grid_3d_can_add, NULL, NULL, xy_grid_3d_post_add, NULL, NULL,
652 { GOG_GRID_XY } },
653 { N_("YZ-Backplane"), "GogGrid", 1,
654 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
655 yz_grid_3d_can_add, NULL, NULL, yz_grid_3d_post_add, NULL, NULL,
656 { GOG_GRID_YZ } },
657 { N_("ZX-Backplane"), "GogGrid", 1,
658 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
659 zx_grid_3d_can_add, NULL, NULL, zx_grid_3d_post_add, NULL, NULL,
660 { GOG_GRID_ZX } },
661 { N_("X-Axis"), "GogAxis", 2,
662 GOG_POSITION_PADDING, GOG_POSITION_PADDING, GOG_OBJECT_NAME_BY_ROLE,
663 x_axis_can_add, axis_can_remove, NULL, x_axis_post_add, axis_pre_remove, NULL,
664 { GOG_AXIS_X } },
665 { N_("Y-Axis"), "GogAxis", 3,
666 GOG_POSITION_PADDING, GOG_POSITION_PADDING, GOG_OBJECT_NAME_BY_ROLE,
667 y_axis_can_add, axis_can_remove, NULL, y_axis_post_add, axis_pre_remove, NULL,
668 { GOG_AXIS_Y } },
669 { N_("Z-Axis"), "GogAxis", 4,
670 GOG_POSITION_PADDING, GOG_POSITION_PADDING, GOG_OBJECT_NAME_BY_ROLE,
671 z_axis_can_add, axis_can_remove, NULL, z_axis_post_add, axis_pre_remove, NULL,
672 { GOG_AXIS_Z } },
673 { N_("Circular-Axis"), "GogAxis", 2,
674 GOG_POSITION_PADDING, GOG_POSITION_PADDING, GOG_OBJECT_NAME_BY_ROLE,
675 circular_axis_can_add, axis_can_remove, NULL, circular_axis_post_add, axis_pre_remove, NULL,
676 { GOG_AXIS_CIRCULAR } },
677 { N_("Radial-Axis"), "GogAxis", 3,
678 GOG_POSITION_PADDING, GOG_POSITION_PADDING, GOG_OBJECT_NAME_BY_ROLE,
679 radial_axis_can_add, axis_can_remove, NULL, radial_axis_post_add, axis_pre_remove, NULL,
680 { GOG_AXIS_RADIAL } },
681 { N_("Pseudo-3D-Axis"), "GogAxis", 4,
682 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
683 pseudo_3d_axis_can_add, axis_can_remove, NULL, pseudo_3d_axis_post_add, axis_pre_remove, NULL,
684 { GOG_AXIS_PSEUDO_3D } },
685 { N_("Bubble-Axis"), "GogAxis", 4,
686 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
687 bubble_axis_can_add, axis_can_remove, NULL, bubble_axis_post_add, axis_pre_remove, NULL,
688 { GOG_AXIS_BUBBLE } },
689 { N_("Color-Axis"), "GogAxis", 4,
690 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
691 color_axis_can_add, axis_can_remove, NULL, color_axis_post_add, axis_pre_remove, NULL,
692 { GOG_AXIS_COLOR } },
693 { N_("Plot"), "GogPlot", 6, /* keep the axis before the plots */
694 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_TYPE,
695 role_plot_can_add, NULL, NULL, role_plot_post_add, role_plot_pre_remove, NULL, { -1 } },
696 { N_("Title"), "GogLabel", 0,
697 GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL,
698 GOG_POSITION_N|GOG_POSITION_ALIGN_CENTER,
699 GOG_OBJECT_NAME_BY_ROLE,
700 NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
701 { N_("Legend"), "GogLegend", 11,
702 GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL,
703 GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER,
704 GOG_OBJECT_NAME_BY_ROLE,
705 NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
706 #ifdef GOFFICE_WITH_LASEM
707 { N_("Equation"), "GogEquation", 11,
708 GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL,
709 GOG_POSITION_S|GOG_POSITION_ALIGN_CENTER,
710 GOG_OBJECT_NAME_BY_ROLE,
711 NULL, NULL, NULL, NULL, NULL, NULL, { -1 } },
712 #endif
713 { N_("3D-Box"), "Gog3DBox", 1,
714 GOG_POSITION_SPECIAL, GOG_POSITION_SPECIAL, GOG_OBJECT_NAME_BY_ROLE,
715 role_3d_box_can_add, role_3d_box_can_remove, NULL, NULL, NULL, NULL, { -1 } },
716 { N_("Color-Scale"), "GogColorScale", 7,
717 GOG_POSITION_COMPASS|GOG_POSITION_ANY_MANUAL|GOG_POSITION_ANY_MANUAL_SIZE,
718 GOG_POSITION_E|GOG_POSITION_ALIGN_CENTER,
719 GOG_OBJECT_NAME_BY_ROLE,
720 color_scale_can_add, NULL, NULL,
721 color_scale_post_add, color_scale_pre_remove, NULL, { -1 } }
722 };
723
724 static GogManualSizeMode
gog_chart_get_manual_size_mode(GogObject * gobj)725 gog_chart_get_manual_size_mode (GogObject *gobj)
726 {
727 return GOG_MANUAL_SIZE_FULL;
728 }
729
730 static void
gog_chart_class_init(GogObjectClass * gog_klass)731 gog_chart_class_init (GogObjectClass *gog_klass)
732 {
733 GObjectClass *gobject_klass = (GObjectClass *)gog_klass;
734
735 chart_parent_klass = g_type_class_peek_parent (gog_klass);
736 gobject_klass->finalize = gog_chart_finalize;
737 gobject_klass->set_property = gog_chart_set_property;
738 gobject_klass->get_property = gog_chart_get_property;
739
740 #ifdef GOFFICE_WITH_GTK
741 gog_klass->populate_editor = gog_chart_populate_editor;
742 #else
743 gog_klass->populate_editor = NULL;
744 #endif
745
746 gog_klass->get_manual_size_mode = gog_chart_get_manual_size_mode;
747
748 g_object_class_install_property (gobject_klass, CHART_PROP_CARDINALITY_VALID,
749 g_param_spec_boolean ("cardinality-valid",
750 _("Valid cardinality"),
751 _("Is the charts cardinality currently valid"),
752 FALSE,
753 GSF_PARAM_STATIC | G_PARAM_READABLE));
754 g_object_class_install_property (gobject_klass, CHART_PROP_PLOT_AREA,
755 g_param_spec_string ("plot-area",
756 _("Plot area"),
757 _("Position and size of plot area, in percentage of chart size"),
758 "0 0 1 1",
759 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
760 g_object_class_install_property (gobject_klass, CHART_PROP_PLOT_AREA_IS_MANUAL,
761 g_param_spec_boolean ("is-plot-area-manual",
762 _("Manual plot area"),
763 _("Is plot area manual"),
764 FALSE,
765 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
766 g_object_class_install_property (gobject_klass, CHART_PROP_X_POS,
767 g_param_spec_int ("xpos", _("xpos"),
768 _("Horizontal chart position in graph grid"),
769 0, G_MAXINT, 0, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
770 /* we need to force saving of ypos since the default is not constant */
771 g_object_class_install_property (gobject_klass, CHART_PROP_Y_POS,
772 g_param_spec_int ("ypos", _("ypos"),
773 _("Vertical chart position in graph grid"),
774 0, G_MAXINT, 0, G_PARAM_READWRITE | GO_PARAM_PERSISTENT | GOG_PARAM_FORCE_SAVE));
775 g_object_class_install_property (gobject_klass, CHART_PROP_COLUMNS,
776 g_param_spec_int ("columns", _("columns"),
777 _("Number of columns in graph grid"),
778 1, G_MAXINT, 1, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
779 g_object_class_install_property (gobject_klass, CHART_PROP_ROWS,
780 g_param_spec_int ("rows", _("rows"),
781 _("Number of rows in graph grid"),
782 1, G_MAXINT, 1, G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
783
784 gog_klass->view_type = gog_chart_view_get_type ();
785 gog_klass->update = gog_chart_update;
786 gog_klass->children_reordered = gog_chart_children_reordered;
787 gog_object_register_roles (gog_klass, roles, G_N_ELEMENTS (roles));
788 }
789
790 static void
gog_chart_init(GogChart * chart)791 gog_chart_init (GogChart *chart)
792 {
793 chart->x_pos =
794 chart->y_pos =
795 chart->cols =
796 chart->rows =
797 chart->x_pos_actual =
798 chart->y_pos_actual = 0;
799
800 /* start as true so that we can queue an update when it changes */
801 chart->cardinality_valid = TRUE;
802 chart->axis_set = GOG_AXIS_SET_UNKNOWN;
803
804 chart->is_plot_area_manual = FALSE;
805 chart->plot_area.x =
806 chart->plot_area.y = 0.0;
807 chart->plot_area.w =
808 chart->plot_area.h = 1.0;
809 }
810
GSF_CLASS(GogChart,gog_chart,gog_chart_class_init,gog_chart_init,GOG_TYPE_OUTLINED_OBJECT)811 GSF_CLASS (GogChart, gog_chart,
812 gog_chart_class_init, gog_chart_init,
813 GOG_TYPE_OUTLINED_OBJECT)
814
815 /**
816 * gog_chart_get_position:
817 * @chart: const #GogChart
818 * @x:
819 * @y:
820 * @cols:
821 * @rows:
822 *
823 * Returns: TRUE if the chart has been positioned.
824 **/
825 gboolean
826 gog_chart_get_position (GogChart const *chart,
827 unsigned *x, unsigned *y, unsigned *cols, unsigned *rows)
828 {
829 g_return_val_if_fail (GOG_CHART (chart), FALSE);
830
831 if (chart->cols <= 0 || chart->rows <= 0)
832 return FALSE;
833
834 if (x != NULL) *x = chart->x_pos;
835 if (y != NULL) *y = chart->y_pos;
836 if (cols != NULL) *cols = chart->cols;
837 if (rows != NULL) *rows = chart->rows;
838
839 return TRUE;
840 }
841
842 /**
843 * gog_chart_set_position:
844 * @chart: #GogChart
845 * @x:
846 * @y:
847 * @cols:
848 * @rows:
849 *
850 **/
851 void
gog_chart_set_position(GogChart * chart,unsigned int x,unsigned int y,unsigned int cols,unsigned int rows)852 gog_chart_set_position (GogChart *chart,
853 unsigned int x, unsigned int y, unsigned int cols, unsigned int rows)
854 {
855 g_return_if_fail (GOG_IS_CHART (chart));
856
857 if (chart->x_pos == x && chart->y_pos == y &&
858 chart->cols == cols && chart->rows == rows)
859 return;
860
861 chart->x_pos = x;
862 chart->y_pos = y;
863 chart->cols = cols;
864 chart->rows = rows;
865
866 gog_graph_validate_chart_layout (GOG_GRAPH (GOG_OBJECT (chart)->parent));
867 gog_object_emit_changed (GOG_OBJECT (chart), TRUE);
868 }
869
870 /**
871 * gog_chart_get_plot_area:
872 * @chart: #GogChart
873 * @plot_area: #GogViewAllocation
874 *
875 * Stores plot area in plot_area, in fraction of chart size.
876 *
877 * Returns: %TRUE if plot area position is manual.
878 **/
879 gboolean
gog_chart_get_plot_area(GogChart * chart,GogViewAllocation * plot_area)880 gog_chart_get_plot_area (GogChart *chart, GogViewAllocation *plot_area)
881 {
882 if (plot_area != NULL)
883 *plot_area = chart->plot_area;
884
885 return chart->is_plot_area_manual;
886 }
887
888 /**
889 * gog_chart_set_plot_area:
890 * @chart: #GogChart
891 * @plot_area: #GogViewAllocation
892 *
893 * If plot_area != NULL, sets plot area size and location, in fraction
894 * of chart size, and sets GogChart::is_plot_area_manual flag to TRUE.
895 * If plot_area == NULL, sets GogChart::is_plot_area_manual to FALSE.
896 **/
897 void
gog_chart_set_plot_area(GogChart * chart,GogViewAllocation const * plot_area)898 gog_chart_set_plot_area (GogChart *chart, GogViewAllocation const *plot_area)
899 {
900 if (plot_area == NULL) {
901 chart->is_plot_area_manual = FALSE;
902 } else {
903 chart->plot_area = *plot_area;
904 chart->is_plot_area_manual = TRUE;
905 }
906 gog_object_emit_changed (GOG_OBJECT (chart), TRUE);
907 }
908
909 /**
910 * gog_chart_get_cardinality:
911 * @chart: a #GogChart
912 * @full: placeholder for full cardinality
913 * @visible: placeholder for visible cardinality
914 *
915 * Update and cache cardinality values if required, and returns
916 * full and visible cardinality. Full cardinality is the number of
917 * chart elements that require a different style. Visible cardinality is
918 * the number of chart elements shown in chart legend.
919 *
920 * @full and @visible may be %NULL.
921 **/
922 void
gog_chart_get_cardinality(GogChart * chart,unsigned * full,unsigned * visible)923 gog_chart_get_cardinality (GogChart *chart, unsigned *full, unsigned *visible)
924 {
925 GSList *ptr;
926 unsigned tmp_full, tmp_visible;
927
928 g_return_if_fail (GOG_IS_CHART (chart));
929
930 if (!chart->cardinality_valid) {
931 chart->cardinality_valid = TRUE;
932 chart->full_cardinality = chart->visible_cardinality = 0;
933 for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next) {
934 gog_plot_update_cardinality (ptr->data, chart->full_cardinality);
935 gog_plot_get_cardinality (ptr->data, &tmp_full, &tmp_visible);
936 chart->full_cardinality += tmp_full;
937 chart->visible_cardinality += tmp_visible;
938 }
939 }
940
941 if (full != NULL)
942 *full = chart->full_cardinality;
943 if (visible != NULL)
944 *visible = chart->visible_cardinality;
945 }
946
947 void
gog_chart_request_cardinality_update(GogChart * chart)948 gog_chart_request_cardinality_update (GogChart *chart)
949 {
950 g_return_if_fail (GOG_IS_CHART (chart));
951
952 if (chart->cardinality_valid) {
953 chart->cardinality_valid = FALSE;
954 gog_object_request_update (GOG_OBJECT (chart));
955 }
956 }
957
958 /**
959 * gog_chart_foreach_elem:
960 * @chart: #GogChart
961 * @only_visible: whether to only apply to visible children
962 * @handler: (scope call): callback
963 * @data: user data
964 *
965 * Applies @handler to children
966 **/
967 void
gog_chart_foreach_elem(GogChart * chart,gboolean only_visible,GogEnumFunc handler,gpointer data)968 gog_chart_foreach_elem (GogChart *chart, gboolean only_visible,
969 GogEnumFunc handler, gpointer data)
970 {
971 GSList *ptr;
972
973 g_return_if_fail (GOG_IS_CHART (chart));
974 g_return_if_fail (chart->cardinality_valid);
975
976 for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
977 gog_plot_foreach_elem (ptr->data, only_visible, handler, data);
978 }
979
980
981 /**
982 * gog_chart_get_plots:
983 * @chart: #GogChart
984 *
985 * Returns: (element-type GogPlot) (transfer none): the list of the plots
986 * in @chart.
987 **/
988 GSList *
gog_chart_get_plots(GogChart const * chart)989 gog_chart_get_plots (GogChart const *chart)
990 {
991 g_return_val_if_fail (GOG_IS_CHART (chart), NULL);
992 return chart->plots;
993 }
994
995 GogAxisSet
gog_chart_get_axis_set(GogChart const * chart)996 gog_chart_get_axis_set (GogChart const *chart)
997 {
998 g_return_val_if_fail (GOG_IS_CHART (chart), GOG_AXIS_SET_UNKNOWN);
999 return chart->axis_set;
1000 }
1001
1002 gboolean
gog_chart_axis_set_is_valid(GogChart const * chart,GogAxisSet type)1003 gog_chart_axis_set_is_valid (GogChart const *chart, GogAxisSet type)
1004 {
1005 GSList *ptr;
1006
1007 g_return_val_if_fail (GOG_IS_CHART (chart), FALSE);
1008
1009 for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
1010 if (!gog_plot_axis_set_is_valid (ptr->data, type))
1011 return FALSE;
1012 return TRUE;
1013 }
1014
1015 static void
gog_chart_add_axis(GogChart * chart,GogAxisType type)1016 gog_chart_add_axis (GogChart *chart, GogAxisType type)
1017 {
1018 unsigned i = G_N_ELEMENTS (roles);
1019 while (i-- > 0)
1020 if (roles[i].user.i == (int)type) {
1021 gog_object_add_by_role (GOG_OBJECT (chart), roles + i, NULL);
1022 return;
1023 }
1024 g_warning ("unknown axis type %d", type);
1025 }
1026
1027 gboolean
gog_chart_axis_set_assign(GogChart * chart,GogAxisSet axis_set)1028 gog_chart_axis_set_assign (GogChart *chart, GogAxisSet axis_set)
1029 {
1030 GogAxis *axis;
1031 GSList *ptr;
1032 GogAxisType type;
1033
1034 g_return_val_if_fail (GOG_IS_CHART (chart), FALSE);
1035
1036 if (chart->axis_set == axis_set)
1037 return TRUE;
1038 chart->axis_set = axis_set;
1039
1040 if (axis_set == GOG_AXIS_SET_UNKNOWN)
1041 return TRUE;
1042
1043 /* Add at least 1 instance of any required axis */
1044 for (type = 0 ; type < GOG_AXIS_TYPES ; type++)
1045 if ((axis_set & (1 << type))) {
1046 GSList *tmp = gog_chart_get_axes (chart, type);
1047 if (tmp == NULL)
1048 gog_chart_add_axis (chart, type);
1049 else
1050 g_slist_free (tmp);
1051 }
1052
1053 /* link the plots */
1054 for (ptr = chart->plots ; ptr != NULL ; ptr = ptr->next)
1055 if (!gog_plot_axis_set_assign (ptr->data, axis_set))
1056 return FALSE;
1057
1058 /* add any existing axis that do not fit this scheme to the set */
1059 for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ) {
1060 axis = ptr->data;
1061 ptr = ptr->next; /* list may change under us */
1062 if (GOG_IS_AXIS (axis)) {
1063 type = -1;
1064 g_object_get (G_OBJECT (axis), "type", &type, NULL);
1065 if (type < 0 || type >= GOG_AXIS_TYPES) {
1066 g_warning ("Invalid axis");
1067 continue;
1068 }
1069
1070 if (0 == (axis_set & (1 << type))) {
1071 /* We used to delete those axes but if the first plot use a
1072 restricted set, all other axes are lost, see #708292 */
1073 chart->axis_set |= 1 << type;
1074 }
1075 }
1076 }
1077
1078 return TRUE;
1079 }
1080
1081 /**
1082 * gog_chart_get_axes:
1083 * @chart: #GogChart
1084 * @target: #GogAxisType
1085 *
1086 * Returns: (element-type GogAxis) (transfer container): a list which the
1087 * caller must free of all axis of type @target
1088 * associated with @chart.
1089 **/
1090 GSList *
gog_chart_get_axes(GogChart const * chart,GogAxisType target)1091 gog_chart_get_axes (GogChart const *chart, GogAxisType target)
1092 {
1093 GSList *ptr, *res = NULL;
1094 GogAxis *axis;
1095 int type;
1096
1097 g_return_val_if_fail (GOG_IS_CHART (chart), NULL);
1098
1099 for (ptr = GOG_OBJECT (chart)->children ; ptr != NULL ; ptr = ptr->next) {
1100 axis = ptr->data;
1101 if (GOG_IS_AXIS (axis)) {
1102 type = -1;
1103 g_object_get (G_OBJECT (axis), "type", &type, NULL);
1104 if (type < 0 || type >= GOG_AXIS_TYPES) {
1105 g_warning ("Invalid axis");
1106 continue;
1107 }
1108 if (type == target)
1109 res = g_slist_prepend (res, axis);
1110 }
1111 }
1112
1113 return res;
1114 }
1115
1116 /**
1117 * gog_chart_get_grid:
1118 * @chart: #GogChart
1119 *
1120 * Returns: (transfer none): the grid associated with @chart if one exists
1121 * otherwise NULL.
1122 **/
1123 GogGrid *
gog_chart_get_grid(GogChart const * chart)1124 gog_chart_get_grid (GogChart const *chart)
1125 {
1126 g_return_val_if_fail (GOG_IS_CHART (chart), NULL);
1127 return GOG_GRID (chart->grid);
1128 }
1129
1130 gboolean
gog_chart_is_3d(GogChart const * chart)1131 gog_chart_is_3d (GogChart const *chart)
1132 {
1133 return chart->axis_set == GOG_AXIS_SET_XYZ;
1134 }
1135
1136 /*********************************************************************/
1137
1138 static void
gog_chart_view_3d_process(GogView * view,GogViewAllocation * bbox)1139 gog_chart_view_3d_process (GogView *view, GogViewAllocation *bbox)
1140 {
1141 /* A XYZ axis set in supposed. If new sets (cylindrical, spherical or
1142 other are added, we'll need to change this code */
1143 GogViewAllocation tmp = *bbox;
1144 GogAxis *axisX, *axisY, *axisZ, *ref = NULL;
1145 GSList *axes;
1146 double xmin, xmax, ymin, ymax, zmin, zmax;
1147 double o[3], x[3], y[3], z[3], tg, d;
1148 Gog3DBox *box;
1149 Gog3DBoxView *box_view;
1150 GogChart *chart = GOG_CHART (gog_view_get_model (view));
1151 GogObject *obj = gog_object_get_child_by_name (GOG_OBJECT (chart), "3D-Box");
1152 GSList *ptr;
1153 GogView *child;
1154 GogViewPadding padding;
1155 GogAxisMetrics xm, ym, zm;
1156
1157 if (!obj) {
1158 obj = g_object_new (GOG_3D_BOX_TYPE, NULL);
1159 gog_object_add_by_name (GOG_OBJECT (chart), "3D-Box", obj);
1160 }
1161 box = GOG_3D_BOX (obj);
1162 box_view = GOG_3D_BOX_VIEW (gog_view_find_child_view (view, obj));
1163
1164 /* Only use the first of the axes. */
1165 axes = gog_chart_get_axes (chart, GOG_AXIS_X);
1166 axisX = GOG_AXIS (axes->data);
1167 xm = gog_axis_get_metrics (axisX);
1168 if (xm != GOG_AXIS_METRICS_DEFAULT)
1169 ref = gog_axis_get_ref_axis (axisX);
1170 g_slist_free (axes);
1171 gog_axis_get_bounds (axisX, &xmin, &xmax);
1172 axes = gog_chart_get_axes (chart, GOG_AXIS_Y);
1173 axisY = GOG_AXIS (axes->data);
1174 ym = gog_axis_get_metrics (axisY);
1175 if (ym != GOG_AXIS_METRICS_DEFAULT && ref == NULL)
1176 ref = gog_axis_get_ref_axis (axisY);
1177 g_slist_free (axes);
1178 gog_axis_get_bounds (axisY, &ymin, &ymax);
1179 axes = gog_chart_get_axes (chart, GOG_AXIS_Z);
1180 axisZ = GOG_AXIS (axes->data);
1181 zm = gog_axis_get_metrics (axisZ);
1182 if (zm != GOG_AXIS_METRICS_DEFAULT && ref == NULL)
1183 ref = gog_axis_get_ref_axis (axisZ);
1184 g_slist_free (axes);
1185 gog_axis_get_bounds (axisZ, &zmin, &zmax);
1186 /* define the 3d box */
1187 if (ref == NULL) {
1188 box_view->dz = tmp.h;
1189 if (ymax - ymin > xmax - xmin) {
1190 box_view->dy = tmp.w;
1191 box_view->dx = (xmax - xmin) / (ymax - ymin) * tmp.w;
1192 } else {
1193 box_view->dx = tmp.w;
1194 box_view->dy = (ymax - ymin) / (xmax - xmin) * tmp.w;
1195 }
1196 } else {
1197 double ref_length, ref_tick_dist, xspan, yspan, zspan;
1198 gog_axis_get_bounds (ref, &ref_length, &xspan);
1199 ref_length -= xspan;
1200 ref_tick_dist = gog_axis_get_major_ticks_distance (ref);
1201 xspan = xmax - xmin;
1202 if (xm == GOG_AXIS_METRICS_RELATIVE_TICKS) {
1203 double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisX);
1204 g_object_get (axisX, "metrics-ratio", &ratio, NULL);
1205 xspan = (xmax - xmin) / tick_dist * ref_tick_dist * ratio;
1206 }
1207 yspan = ymax - ymin;
1208 if (ym == GOG_AXIS_METRICS_RELATIVE_TICKS) {
1209 double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisY);
1210 g_object_get (axisY, "metrics-ratio", &ratio, NULL);
1211 yspan = (ymax - ymin) / tick_dist * ref_tick_dist * ratio;
1212 }
1213 zspan = zmax - zmin;
1214 if (zm == GOG_AXIS_METRICS_RELATIVE_TICKS) {
1215 double ratio, tick_dist = gog_axis_get_major_ticks_distance (axisZ);
1216 g_object_get (axisZ, "metrics-ratio", &ratio, NULL);
1217 zspan = (zmax - zmin) / tick_dist * ref_tick_dist * ratio;
1218 }
1219 if (ref == axisZ) {
1220 gboolean xrel = FALSE;
1221 box_view->dz = tmp.h;
1222 switch (xm) {
1223 case GOG_AXIS_METRICS_RELATIVE:
1224 case GOG_AXIS_METRICS_RELATIVE_TICKS:
1225 box_view->dx = xspan / zspan * tmp.h;
1226 if (box_view->dx > tmp.w) {
1227 box_view->dz *= tmp.w / box_view->dx;
1228 box_view->dx = tmp.w;
1229 }
1230 xrel = TRUE;
1231 break;
1232 default:
1233 box_view->dx = tmp.w;
1234 break;
1235 }
1236 switch (ym) {
1237 case GOG_AXIS_METRICS_RELATIVE:
1238 case GOG_AXIS_METRICS_RELATIVE_TICKS:
1239 box_view->dy = yspan / zspan * box_view->dz;
1240 if (box_view->dy > tmp.w) {
1241 box_view->dz *= tmp.w / box_view->dy;
1242 if (xrel)
1243 box_view->dx *= tmp.w / box_view->dy;
1244 box_view->dy = tmp.w;
1245 }
1246 break;
1247 default:
1248 box_view->dy = tmp.w;
1249 break;
1250 }
1251 } else {
1252 if (yspan > xspan) {
1253 box_view->dy = tmp.w;
1254 box_view->dx = xspan / yspan * tmp.w;
1255 } else {
1256 box_view->dx = tmp.w;
1257 box_view->dy = yspan / xspan * tmp.w;
1258 }
1259 if (zm == GOG_AXIS_METRICS_DEFAULT)
1260 box_view->dz = tmp.h;
1261 else
1262 box_view->dz = (ref == axisX)?
1263 zspan / xspan * box_view->dx:
1264 zspan / yspan * box_view->dy;
1265 }
1266 }
1267
1268 /* now compute the position of each vertex, ignoring the fov */
1269 go_matrix3x3_transform (&box->mat, -box_view->dx, -box_view->dy, -box_view->dz, o, o + 1, o + 2);
1270 go_matrix3x3_transform (&box->mat, box_view->dx, -box_view->dy, -box_view->dz, x, x + 1, x + 2);
1271 go_matrix3x3_transform (&box->mat, -box_view->dx, box_view->dy, -box_view->dz, y, y + 1, y + 2);
1272 go_matrix3x3_transform (&box->mat, -box_view->dx, -box_view->dy, box_view->dz, z, z + 1, z + 2);
1273 /* for each diagonal, we need to take the vertex closer to the view point */
1274 if (o[1] > 0) {
1275 o[0] = -o[0];
1276 o[1] = -o[1];
1277 o[2] = -o[2];
1278 }
1279 if (x[1] > 0) {
1280 x[0] = -x[0];
1281 x[1] = -x[1];
1282 x[2] = -x[2];
1283 }
1284 if (y[1] > 0) {
1285 y[0] = -y[0];
1286 y[1] = -y[1];
1287 y[2] = -y[2];
1288 }
1289 if (z[1] > 0) {
1290 z[0] = -z[0];
1291 z[1] = -z[1];
1292 z[2] = -z[2];
1293 }
1294 /* if the fov is positive, calculate the position of the viewpoint */
1295 if (box->fov > 0.) {
1296 tg = tan (box->fov / 2.);
1297 box_view->r = -sqrt (o[0] * o[0] + o[2] * o[2]) / tg + o[1];
1298 d = -sqrt (x[0] * x[0] + x[2] * x[2]) / tg + x[1];
1299 if (d < box_view->r)
1300 box_view->r = d;
1301 d = -sqrt (y[0] * y[0] + y[2] * y[2]) / tg + y[1];
1302 if (d < box_view->r)
1303 box_view->r = d;
1304 d = -sqrt (z[0] *z[0] + z[2] * z[2]) / tg + z[1];
1305 if (d < box_view->r)
1306 box_view->r = d;
1307 /* also calculate the reduction factor we need to make things fit in the bbox */
1308 xmax = fabs (o[0]) / (1. - o[1] / box_view->r);
1309 zmax = fabs (o[2]) / (1. - o[1] / box_view->r);
1310 d = fabs (x[0]) / (1. - x[1] / box_view->r);
1311 if (d > xmax)
1312 xmax = d;
1313 d = fabs (x[2]) / (1. - x[1] / box_view->r);
1314 if (d > zmax)
1315 zmax = d;
1316 d = fabs (y[0]) / (1. - y[1] / box_view->r);
1317 if (d > xmax)
1318 xmax = d;
1319 d = fabs (y[2]) / (1. - y[1] / box_view->r);
1320 if (d > zmax)
1321 zmax = d;
1322 d = fabs (z[0]) / (1. - z[1] / box_view->r);
1323 if (d > xmax)
1324 xmax = d;
1325 d = fabs (z[2]) / (1. - z[1] / box_view->r);
1326 if (d > zmax)
1327 zmax = d;
1328 } else {
1329 /* calculate the reduction factor we need to make things fit in the bbox */
1330 xmax = fabs (o[0]);
1331 zmax = fabs (o[2]);
1332 d = fabs (x[0]);
1333 if (d > xmax)
1334 xmax = d;
1335 d = fabs (x[2]);
1336 if (d > zmax)
1337 zmax = d;
1338 d = fabs (y[0]);
1339 if (d > xmax)
1340 xmax = d;
1341 d = fabs (y[2]);
1342 if (d > zmax)
1343 zmax = d;
1344 d = fabs (z[0]);
1345 if (d > xmax)
1346 xmax = d;
1347 d = fabs (z[2]);
1348 if (d > zmax)
1349 zmax = d;
1350 }
1351 /* use d and tg as x and z ratios, respectively */
1352 d = xmax / tmp.w;
1353 tg = zmax / tmp.h;
1354 box_view->ratio = (d > tg)? d: tg;
1355
1356 gog_view_padding_request (view, bbox, &padding);
1357
1358 if (!chart->is_plot_area_manual) {
1359 bbox->x += padding.wl;
1360 bbox->w -= padding.wl + padding.wr;
1361 bbox->y += padding.ht;
1362 bbox->h -= padding.ht + padding.hb;
1363 } else {
1364 tmp.x -= padding.wl;
1365 tmp.w += padding.wl + padding.wr;
1366 tmp.y -= padding.ht;
1367 tmp.h += padding.ht + padding.hb;
1368 }
1369
1370 /* Recalculating ratio */
1371 d = xmax / bbox->w;
1372 tg = zmax / bbox->h;
1373 box_view->ratio = (d > tg)? d: tg;
1374
1375 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
1376 child = ptr->data;
1377 if (GOG_POSITION_IS_PADDING (child->model->position)) {
1378 gog_view_size_allocate (child, &tmp);
1379 }
1380 }
1381
1382 /* by default, overlay all GOG_POSITION_SPECIAL children in residual */
1383 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
1384 child = ptr->data;
1385 if (GOG_POSITION_IS_SPECIAL (child->model->position))
1386 gog_view_size_allocate (child, bbox);
1387 }
1388 }
1389
1390 typedef struct {
1391 GogOutlinedView base;
1392
1393 GogViewAllocation plot_area;
1394 } GogChartView;
1395 typedef GogOutlinedViewClass GogChartViewClass;
1396
1397 #define GOG_TYPE_CHART_VIEW (gog_chart_view_get_type ())
1398 #define GOG_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GOG_TYPE_CHART_VIEW, GogChartView))
1399 #define GOG_IS_CHART_VIEW(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GOG_TYPE_CHART_VIEW))
1400
1401 static GogViewClass *cview_parent_klass;
1402
1403 static void
gog_chart_view_size_allocate(GogView * view,GogViewAllocation const * bbox)1404 gog_chart_view_size_allocate (GogView *view, GogViewAllocation const *bbox)
1405 {
1406 GSList *ptr;
1407 GogView *child;
1408 GogChartView *chart_view = GOG_CHART_VIEW (view);
1409 GogViewAllocation tmp, *plot_area = &chart_view->plot_area;
1410 GogViewPadding padding;
1411 GogChart *chart = GOG_CHART (gog_view_get_model (view));
1412
1413 (cview_parent_klass->size_allocate) (view, bbox);
1414
1415 if (chart->is_plot_area_manual) {
1416 plot_area->x = bbox->x + chart->plot_area.x * bbox->w;
1417 plot_area->y = bbox->y + chart->plot_area.y * bbox->h;
1418 plot_area->w = chart->plot_area.w * bbox->w;
1419 plot_area->h = chart->plot_area.h * bbox->h;
1420 } else
1421 *plot_area = view->residual;
1422
1423 /* special treatment for 3d charts */
1424 if (gog_chart_is_3d (chart)) {
1425 gog_chart_view_3d_process (view, plot_area);
1426 return;
1427 }
1428
1429 tmp = *plot_area;
1430 gog_view_padding_request (view, plot_area, &padding);
1431
1432 if (!chart->is_plot_area_manual) {
1433 plot_area->x += padding.wl;
1434 plot_area->w -= padding.wl + padding.wr;
1435 plot_area->y += padding.ht;
1436 plot_area->h -= padding.ht + padding.hb;
1437 } else {
1438 tmp.x -= padding.wl;
1439 tmp.w += padding.wl + padding.wr;
1440 tmp.y -= padding.ht;
1441 tmp.h += padding.ht + padding.hb;
1442 }
1443
1444 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
1445 child = ptr->data;
1446 if (GOG_POSITION_IS_PADDING (child->model->position)) {
1447 gog_view_size_allocate (child, &tmp);
1448 }
1449 }
1450
1451 /* by default, overlay all GOG_POSITION_SPECIAL children in residual */
1452 for (ptr = view->children; ptr != NULL ; ptr = ptr->next) {
1453 child = ptr->data;
1454 if (GOG_POSITION_IS_SPECIAL (child->model->position))
1455 gog_view_size_allocate (child, plot_area);
1456 }
1457 }
1458
1459 static void
gog_chart_view_init(GogChartView * cview)1460 gog_chart_view_init (GogChartView *cview)
1461 {
1462 }
1463
1464 static void
grid_line_render(GSList * start_ptr,GogViewAllocation const * bbox)1465 grid_line_render (GSList *start_ptr, GogViewAllocation const *bbox)
1466 {
1467 GSList *ptr, *child_ptr;
1468 GSList *minor_grid_lines = NULL;
1469 GSList *major_grid_lines = NULL;
1470 GogView *child_view, *axis_child_view;
1471
1472 for (ptr = start_ptr; ptr != NULL; ptr = ptr->next) {
1473 child_view = ptr->data;
1474 if (GOG_IS_AXIS (child_view->model)) {
1475 for (child_ptr = child_view->children; child_ptr != NULL; child_ptr = child_ptr->next) {
1476 axis_child_view = child_ptr->data;
1477 if (GOG_IS_GRID_LINE (axis_child_view->model)) {
1478 if (gog_grid_line_is_minor (GOG_GRID_LINE (axis_child_view->model)))
1479 minor_grid_lines = g_slist_prepend (minor_grid_lines,
1480 axis_child_view);
1481 else
1482 major_grid_lines = g_slist_prepend (major_grid_lines,
1483 axis_child_view);
1484 }
1485 else if (GOG_IS_AXIS_LINE (axis_child_view->model)) {
1486 GogView *grid_child_view;
1487 GSList *lptr;
1488 for (lptr = axis_child_view->children; lptr != NULL; lptr = lptr->next) {
1489 grid_child_view = lptr->data;
1490 if (GOG_IS_GRID_LINE (grid_child_view->model)) {
1491 if (gog_grid_line_is_minor (GOG_GRID_LINE (grid_child_view->model)))
1492 minor_grid_lines = g_slist_prepend (minor_grid_lines,
1493 grid_child_view);
1494 else
1495 major_grid_lines = g_slist_prepend (major_grid_lines,
1496 grid_child_view);
1497 }
1498
1499 }
1500 }
1501 }
1502 }
1503 }
1504
1505 /* Render stripes, minor first then major */
1506 for (ptr = minor_grid_lines; ptr != NULL; ptr = ptr->next) {
1507 gog_grid_line_view_render_stripes (ptr->data);
1508 }
1509 for (ptr = major_grid_lines; ptr != NULL; ptr = ptr->next) {
1510 gog_grid_line_view_render_stripes (ptr->data);
1511 }
1512
1513 /* Render lines, minor first then major */
1514 for (ptr = minor_grid_lines; ptr != NULL; ptr = ptr->next) {
1515 gog_grid_line_view_render_lines (ptr->data);
1516 }
1517 for (ptr = major_grid_lines; ptr != NULL; ptr = ptr->next) {
1518 gog_grid_line_view_render_lines (ptr->data);
1519 }
1520
1521 g_slist_free (minor_grid_lines);
1522 g_slist_free (major_grid_lines);
1523 }
1524
1525 static void
plot_render(GogView * view,GogViewAllocation const * bbox,GogPlotRenderingOrder order)1526 plot_render (GogView *view, GogViewAllocation const *bbox, GogPlotRenderingOrder order)
1527 {
1528 GSList *ptr;
1529 GogView *child_view;
1530
1531 /* Render some plots before axes */
1532 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1533 child_view = ptr->data;
1534 if (GOG_IS_PLOT (child_view->model) &&
1535 GOG_PLOT (child_view->model)->rendering_order == order)
1536 gog_view_render (ptr->data, bbox);
1537 }
1538 }
1539
1540 static void
gog_chart_view_render(GogView * view,GogViewAllocation const * bbox)1541 gog_chart_view_render (GogView *view, GogViewAllocation const *bbox)
1542 {
1543 GSList *ptr;
1544 GogView *child_view;
1545 gboolean grid_line_rendered = FALSE;
1546 GogChart *chart = GOG_CHART (gog_view_get_model (view));
1547
1548 cview_parent_klass->render (view, bbox);
1549
1550 if (gog_chart_is_3d (chart)) {
1551 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1552 child_view = ptr->data;
1553 if (!GOG_IS_AXIS (child_view->model) && !GOG_IS_PLOT (child_view->model) && !GOG_IS_LABEL (child_view->model))
1554 gog_view_render (ptr->data, bbox);
1555 }
1556 /* now render plot and axes */
1557 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1558 child_view = ptr->data;
1559 if (!GOG_IS_AXIS (child_view->model))
1560 continue;
1561 gog_view_render (ptr->data, bbox);
1562 grid_line_render (ptr, bbox);
1563 }
1564 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1565 child_view = ptr->data;
1566 if (!GOG_IS_PLOT (child_view->model))
1567 continue;
1568 gog_view_render (ptr->data, bbox);
1569 }
1570 } else {
1571 /* KLUDGE: render grid lines before axis */
1572 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1573 child_view = ptr->data;
1574 if (!grid_line_rendered && GOG_IS_AXIS (child_view->model)) {
1575 plot_render (view, bbox, GOG_PLOT_RENDERING_BEFORE_GRID);
1576 grid_line_render (ptr, bbox);
1577 plot_render (view, bbox, GOG_PLOT_RENDERING_BEFORE_AXIS);
1578 grid_line_rendered = TRUE;
1579 }
1580 if (GOG_IS_PLOT (child_view->model)) {
1581 if (!GOG_PLOT (child_view->model)->rendering_order)
1582 gog_view_render (ptr->data, bbox);
1583 } else if (!GOG_IS_LABEL (child_view->model))
1584 gog_view_render (ptr->data, bbox);
1585 }
1586 }
1587 for (ptr = view->children ; ptr != NULL ; ptr = ptr->next) {
1588 child_view = ptr->data;
1589 if (!GOG_IS_LABEL (child_view->model))
1590 continue;
1591 gog_view_render (ptr->data, bbox);
1592 }
1593 }
1594
1595 static void
gog_chart_view_class_init(GogChartViewClass * gview_klass)1596 gog_chart_view_class_init (GogChartViewClass *gview_klass)
1597 {
1598 GogViewClass *view_klass = (GogViewClass *) gview_klass;
1599 GogOutlinedViewClass *oview_klass = (GogOutlinedViewClass *) gview_klass;
1600
1601 cview_parent_klass = g_type_class_peek_parent (gview_klass);
1602
1603 view_klass->size_allocate = gog_chart_view_size_allocate;
1604 view_klass->render = gog_chart_view_render;
1605 view_klass->clip = FALSE;
1606 oview_klass->call_parent_render = FALSE;
1607 }
1608
GSF_CLASS(GogChartView,gog_chart_view,gog_chart_view_class_init,gog_chart_view_init,GOG_TYPE_OUTLINED_VIEW)1609 static GSF_CLASS (GogChartView, gog_chart_view,
1610 gog_chart_view_class_init, gog_chart_view_init,
1611 GOG_TYPE_OUTLINED_VIEW)
1612
1613 GogViewAllocation const *
1614 gog_chart_view_get_plot_area (GogView const *view)
1615 {
1616 g_return_val_if_fail (GOG_IS_CHART_VIEW (view), NULL);
1617
1618 return &(GOG_CHART_VIEW (view)->plot_area);
1619 }
1620