1 /*
2 * go-pie.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 "gog-pie.h"
24 #include <goffice/graph/gog-view.h>
25 #include <goffice/graph/gog-renderer.h>
26 #include <goffice/graph/gog-theme.h>
27 #include <goffice/data/go-data.h>
28 #include <goffice/math/go-math.h>
29 #include <goffice/utils/go-color.h>
30 #include <goffice/utils/go-persist.h>
31 #include <goffice/utils/go-style.h>
32 #include <goffice/utils/go-styled-object.h>
33 #include <goffice/app/module-plugin-defs.h>
34
35 #include <glib/gi18n-lib.h>
36 #include <gsf/gsf-impl-utils.h>
37 #include <math.h>
38 #include <string.h>
39
40 #include "embedded-stuff.c"
41
42 static GObjectClass *ppe_parent_klass;
43
44 typedef GogSeriesElementClass GogPieSeriesElementClass;
45 enum {
46 ELEMENT_PROP_0,
47 ELEMENT_SEPARATION,
48 };
49
50 static void
gog_pie_series_element_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)51 gog_pie_series_element_set_property (GObject *obj, guint param_id,
52 GValue const *value, GParamSpec *pspec)
53 {
54 GogPieSeriesElement *pse = GOG_PIE_SERIES_ELEMENT (obj);
55
56 switch (param_id) {
57 case ELEMENT_SEPARATION:
58 pse->separation = g_value_get_double (value);
59 break;
60
61 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
62 return; /* NOTE : RETURN */
63 }
64
65 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
66 }
67
68 static void
gog_pie_series_element_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)69 gog_pie_series_element_get_property (GObject *obj, guint param_id,
70 GValue *value, GParamSpec *pspec)
71 {
72 GogPieSeriesElement *pse = GOG_PIE_SERIES_ELEMENT (obj);
73
74 switch (param_id) {
75 case ELEMENT_SEPARATION:
76 g_value_set_double (value, pse->separation);
77 break;
78 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
79 break;
80 }
81 }
82
83 #ifdef GOFFICE_WITH_GTK
84 extern gpointer gog_pie_series_element_pref (GogPieSeriesElement *element, GOCmdContext *cc);
85 static gpointer
gog_pie_series_element_populate_editor(GogObject * gobj,GOEditor * editor,GOCmdContext * cc)86 gog_pie_series_element_populate_editor (GogObject *gobj,
87 GOEditor *editor,
88 GOCmdContext *cc)
89 {
90 GtkWidget *widget = gog_pie_series_element_pref (GOG_PIE_SERIES_ELEMENT (gobj), cc);
91 go_editor_add_page (editor, widget, _("Settings"));
92 g_object_unref (widget);
93 return widget;
94 }
95 #endif
96
97 static void
gog_pie_series_element_class_init(GogPieSeriesElementClass * klass)98 gog_pie_series_element_class_init (GogPieSeriesElementClass *klass)
99 {
100 GObjectClass *gobject_klass = (GObjectClass *) klass;
101
102 gobject_klass->set_property = gog_pie_series_element_set_property;
103 gobject_klass->get_property = gog_pie_series_element_get_property;
104
105 ppe_parent_klass = g_type_class_peek_parent (klass);
106 #ifdef GOFFICE_WITH_GTK
107 klass->gse_populate_editor = gog_pie_series_element_populate_editor;
108 #endif
109
110 g_object_class_install_property (gobject_klass, ELEMENT_SEPARATION,
111 g_param_spec_double ("separation",
112 _("Separation"),
113 _("Amount a slice is extended as a percentage of the radius"),
114 0, 1000, 0.,
115 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
116 }
117
118 GSF_DYNAMIC_CLASS (GogPieSeriesElement, gog_pie_series_element,
119 gog_pie_series_element_class_init, NULL /*gog_pie_series_element_init*/,
120 GOG_TYPE_SERIES_ELEMENT)
121
122 /*****************************************************************************/
123
124 typedef enum {
125 GOG_SHOW_NEGS_SKIP,
126 GOG_SHOW_NEGS_ABSOLUTE,
127 GOG_SHOW_NEGS_WHITE,
128 GOG_SHOW_NEGS_INVERTED, /* not used for now */
129 GOG_SHOW_NEGS_MAX
130 } GogShowNegsMode;
131
132 static struct {
133 GogShowNegsMode mode;
134 char const *name;
135 } neg_modes [GOG_SHOW_NEGS_MAX] = {
136 {GOG_SHOW_NEGS_SKIP, "skip"},
137 {GOG_SHOW_NEGS_ABSOLUTE, "absolute"},
138 {GOG_SHOW_NEGS_WHITE, "white"},
139 {GOG_SHOW_NEGS_INVERTED, "inverted"}
140 };
141
142 static GogShowNegsMode
gog_show_neg_mode_from_str(char const * name)143 gog_show_neg_mode_from_str (char const *name)
144 {
145 unsigned i;
146 GogShowNegsMode ret = GOG_SHOW_NEGS_ABSOLUTE;
147
148 for (i = 0; i < GO_LINE_MAX; i++) {
149 if (strcmp (neg_modes[i].name, name) == 0) {
150 ret = neg_modes[i].mode;
151 break;
152 }
153 }
154 return ret;
155 }
156
157 static char const *
gog_show_neg_mode_as_str(GogShowNegsMode mode)158 gog_show_neg_mode_as_str (GogShowNegsMode mode)
159 {
160 unsigned i;
161 char const *ret = "absolute";
162
163 for (i = 0; i < GOG_SHOW_NEGS_MAX; i++) {
164 if (neg_modes[i].mode == mode) {
165 ret = neg_modes[i].name;
166 break;
167 }
168 }
169 return ret;
170 }
171
172 typedef struct {
173 GogPlotClass base;
174 } GogPiePlotClass;
175
176 enum {
177 PLOT_PROP_0,
178 PLOT_PROP_INITIAL_ANGLE,
179 PLOT_PROP_DEFAULT_SEPARATION,
180 PLOT_PROP_IN_3D,
181 PLOT_PROP_SPAN,
182 PLOT_PROP_SHOW_NEGS
183 };
184
185 GOFFICE_PLUGIN_MODULE_HEADER;
186
187 static GObjectClass *pie_parent_klass;
188 static GType gog_pie_view_get_type (void);
189
190 static void
gog_pie_plot_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)191 gog_pie_plot_set_property (GObject *obj, guint param_id,
192 GValue const *value, GParamSpec *pspec)
193 {
194 GogPiePlot *pie = GOG_PIE_PLOT (obj);
195
196 switch (param_id) {
197 case PLOT_PROP_INITIAL_ANGLE: {
198 double a = g_value_get_double (value);
199 a = fmod (a, 360);
200 if (a < 0) a += 360;
201 pie->initial_angle = a;
202 break;
203 }
204 case PLOT_PROP_DEFAULT_SEPARATION: {
205 double f = g_value_get_double (value);
206 pie->default_separation = CLAMP (f, 0., 5.);
207 break;
208 }
209 case PLOT_PROP_IN_3D:
210 pie->in_3d = g_value_get_boolean (value);
211 break;
212 case PLOT_PROP_SPAN:
213 pie->span = g_value_get_double (value);
214 break;
215 case PLOT_PROP_SHOW_NEGS : {
216 GSList *ptr = GOG_PLOT (obj)->series;
217 pie->show_negatives = gog_show_neg_mode_from_str (g_value_get_string (value));
218 /* we need to update all the series */
219 while (ptr) {
220 gog_object_request_update (GOG_OBJECT (ptr->data));
221 ptr = ptr->next;
222 }
223 break;
224 }
225
226 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
227 return; /* NOTE : RETURN */
228 }
229
230 /* none of the attributes triggers a size change yet.
231 * When we add data labels we'll need it */
232 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
233 }
234
235 static void
gog_pie_plot_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)236 gog_pie_plot_get_property (GObject *obj, guint param_id,
237 GValue *value, GParamSpec *pspec)
238 {
239 GogPiePlot *pie = GOG_PIE_PLOT (obj);
240
241 switch (param_id) {
242 case PLOT_PROP_INITIAL_ANGLE:
243 g_value_set_double (value, pie->initial_angle);
244 break;
245 case PLOT_PROP_DEFAULT_SEPARATION:
246 g_value_set_double (value, pie->default_separation);
247 break;
248 case PLOT_PROP_IN_3D:
249 g_value_set_boolean (value, pie->in_3d);
250 break;
251 case PLOT_PROP_SPAN:
252 g_value_set_double (value, pie->span);
253 break;
254 case PLOT_PROP_SHOW_NEGS:
255 g_value_set_string (value, gog_show_neg_mode_as_str (pie->show_negatives));
256 break;
257 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
258 break;
259 }
260 }
261
262 static char const *
gog_pie_plot_type_name(G_GNUC_UNUSED GogObject const * item)263 gog_pie_plot_type_name (G_GNUC_UNUSED GogObject const *item)
264 {
265 return N_("PlotPie");
266 }
267
268 #ifdef GOFFICE_WITH_GTK
269 extern gpointer gog_pie_plot_pref (GogPiePlot *pie, GOCmdContext *cc);
270 static void
gog_pie_plot_populate_editor(GogObject * item,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)271 gog_pie_plot_populate_editor (GogObject *item,
272 GOEditor *editor,
273 G_GNUC_UNUSED GogDataAllocator *dalloc,
274 GOCmdContext *cc)
275 {
276 GtkWidget *widget = gog_pie_plot_pref (GOG_PIE_PLOT (item), cc);
277 go_editor_add_page (editor,
278 widget,
279 _("Properties"));
280 g_object_unref (widget);
281
282 (GOG_OBJECT_CLASS(pie_parent_klass)->populate_editor) (item, editor, dalloc, cc);
283 }
284 #endif
285
286 static void
gog_pie_plot_update(GogObject * obj)287 gog_pie_plot_update (GogObject *obj)
288 {
289 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
290 }
291
292 static void
gog_pie_plot_class_init(GogPlotClass * plot_klass)293 gog_pie_plot_class_init (GogPlotClass *plot_klass)
294 {
295 GObjectClass *gobject_klass = (GObjectClass *) plot_klass;
296 GogObjectClass *gog_klass = (GogObjectClass *) plot_klass;
297
298 pie_parent_klass = g_type_class_peek_parent (plot_klass);
299 gobject_klass->set_property = gog_pie_plot_set_property;
300 gobject_klass->get_property = gog_pie_plot_get_property;
301
302 gog_klass->update = gog_pie_plot_update;
303 gog_klass->type_name = gog_pie_plot_type_name;
304 #ifdef GOFFICE_WITH_GTK
305 gog_klass->populate_editor = gog_pie_plot_populate_editor;
306 #endif
307 gog_klass->view_type = gog_pie_view_get_type ();
308
309 g_object_class_install_property (gobject_klass, PLOT_PROP_INITIAL_ANGLE,
310 g_param_spec_double ("initial-angle",
311 _("Initial angle"),
312 _("Degrees clockwise from 12 O'Clock."),
313 -G_MAXFLOAT, G_MAXFLOAT, 0.,
314 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
315 g_object_class_install_property (gobject_klass, PLOT_PROP_DEFAULT_SEPARATION,
316 g_param_spec_double ("default-separation",
317 _("Default separation"),
318 _("Default amount a slice is extended as a percentage of the radius"),
319 0, G_MAXFLOAT, 0.,
320 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
321 g_object_class_install_property (gobject_klass, PLOT_PROP_IN_3D,
322 g_param_spec_boolean ("in-3d",
323 _("In 3D"),
324 _("Draw 3DS wedges"),
325 FALSE,
326 GSF_PARAM_STATIC | G_PARAM_READWRITE));
327 g_object_class_install_property (gobject_klass, PLOT_PROP_SPAN,
328 g_param_spec_double ("span",
329 _("Span"),
330 _("Total angle used as a percentage of the full circle"),
331 10., 100., 100.,
332 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
333 g_object_class_install_property (gobject_klass, PLOT_PROP_SHOW_NEGS,
334 g_param_spec_string ("show-negs",
335 _("Show negative values"),
336 _("How negative values are displayed"),
337 "absolute",
338 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
339
340 {
341 static GogSeriesDimDesc dimensions[] = {
342 { N_("Labels"), GOG_SERIES_SUGGESTED, TRUE,
343 GOG_DIM_LABEL, GOG_MS_DIM_CATEGORIES },
344 { N_("Values"), GOG_SERIES_REQUIRED, FALSE,
345 GOG_DIM_VALUE, GOG_MS_DIM_VALUES }
346 };
347 plot_klass->desc.series.dim = dimensions;
348 plot_klass->desc.series.num_dim = G_N_ELEMENTS (dimensions);
349 plot_klass->desc.series.style_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
350 }
351 plot_klass->desc.num_series_max = 1;
352 plot_klass->series_type = gog_pie_series_get_type ();
353 }
354
355 static void
gog_pie_plot_init(GogPiePlot * pie)356 gog_pie_plot_init (GogPiePlot *pie)
357 {
358 pie->base.vary_style_by_element = TRUE;
359 pie->span = 100.;
360 pie->show_negatives = GOG_SHOW_NEGS_ABSOLUTE;
361 }
362
GSF_DYNAMIC_CLASS(GogPiePlot,gog_pie_plot,gog_pie_plot_class_init,gog_pie_plot_init,GOG_TYPE_PLOT)363 GSF_DYNAMIC_CLASS (GogPiePlot, gog_pie_plot,
364 gog_pie_plot_class_init, gog_pie_plot_init,
365 GOG_TYPE_PLOT)
366
367 #ifdef GOFFICE_WITH_GTK
368
369 static void
370 gog_pie_plot_set_default_separation (GogPiePlot *pie, double separation)
371 {
372 g_return_if_fail (GOG_PIE_PLOT (pie) != NULL);
373
374 pie->default_separation = CLAMP (separation, 0.0, 5.0);
375 gog_object_emit_changed (GOG_OBJECT (pie), FALSE);
376 }
377
378 #endif
379
380 /*****************************************************************************/
381
382 enum {
383 RING_PLOT_PROP_0,
384 RING_PLOT_PROP_CENTER_SIZE,
385 };
386
387 typedef GogPiePlotClass GogRingPlotClass;
388 static GObjectClass *ring_parent_klass;
389
390 static void
gog_ring_plot_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)391 gog_ring_plot_set_property (GObject *obj, guint param_id,
392 GValue const *value, GParamSpec *pspec)
393 {
394 GogRingPlot *ring = GOG_RING_PLOT (obj);
395
396 switch (param_id) {
397 case RING_PLOT_PROP_CENTER_SIZE:
398 ring->center_size = g_value_get_double (value);
399 break;
400
401 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
402 return; /* NOTE : RETURN */
403 }
404
405 /* none of the attributes triggers a size change yet.
406 * When we add data labels we'll need it */
407 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
408 }
409
410 static void
gog_ring_plot_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)411 gog_ring_plot_get_property (GObject *obj, guint param_id,
412 GValue *value, GParamSpec *pspec)
413 {
414 GogRingPlot *ring = GOG_RING_PLOT (obj);
415
416 switch (param_id) {
417 case RING_PLOT_PROP_CENTER_SIZE:
418 g_value_set_double (value, ring->center_size);
419 break;
420 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
421 break;
422 }
423 }
424
425 static char const *
gog_ring_plot_type_name(G_GNUC_UNUSED GogObject const * item)426 gog_ring_plot_type_name (G_GNUC_UNUSED GogObject const *item)
427 {
428 return N_("PlotRing");
429 }
430
431 #ifdef GOFFICE_WITH_GTK
432 extern gpointer gog_ring_plot_pref (GogRingPlot *ring, GOCmdContext *cc);
433 static void
gog_ring_plot_populate_editor(GogObject * item,GOEditor * editor,G_GNUC_UNUSED GogDataAllocator * dalloc,GOCmdContext * cc)434 gog_ring_plot_populate_editor (GogObject *item,
435 GOEditor *editor,
436 G_GNUC_UNUSED GogDataAllocator *dalloc,
437 GOCmdContext *cc)
438 {
439 GtkWidget *widget = gog_ring_plot_pref (GOG_RING_PLOT (item), cc);
440 go_editor_add_page (editor,
441 widget,
442 _("Properties"));
443 g_object_unref (widget);
444 }
445 #endif
446
447 static void
gog_ring_plot_class_init(GogPiePlotClass * pie_plot_klass)448 gog_ring_plot_class_init (GogPiePlotClass *pie_plot_klass)
449 {
450 GObjectClass *gobject_klass = (GObjectClass *) pie_plot_klass;
451 GogObjectClass *gog_klass = (GogObjectClass *) pie_plot_klass;
452 GogPlotClass *plot_klass = (GogPlotClass *) pie_plot_klass;
453
454 ring_parent_klass = g_type_class_peek_parent (pie_plot_klass);
455 gobject_klass->set_property = gog_ring_plot_set_property;
456 gobject_klass->get_property = gog_ring_plot_get_property;
457
458 gog_klass->type_name = gog_ring_plot_type_name;
459 #ifdef GOFFICE_WITH_GTK
460 gog_klass->populate_editor = gog_ring_plot_populate_editor;
461 #endif
462
463 g_object_class_install_property (gobject_klass, RING_PLOT_PROP_CENTER_SIZE,
464 g_param_spec_double ("center-size",
465 _("Center-size"),
466 _("Size of the center hole as a percentage of the radius"),
467 0, 100., 0.5,
468 GSF_PARAM_STATIC | G_PARAM_READWRITE | GO_PARAM_PERSISTENT));
469
470 plot_klass->desc.num_series_max = G_MAXINT;
471 plot_klass->desc.series.style_fields = GO_STYLE_OUTLINE | GO_STYLE_FILL;
472 }
473
474 static void
gog_ring_plot_init(GogRingPlot * ring)475 gog_ring_plot_init (GogRingPlot *ring)
476 {
477 ring->center_size = 0.5;
478 }
479
480 GSF_DYNAMIC_CLASS (GogRingPlot, gog_ring_plot,
481 gog_ring_plot_class_init, gog_ring_plot_init,
482 GOG_TYPE_PIE_PLOT)
483
484 /*****************************************************************************/
485
486 #ifdef GOFFICE_WITH_GTK
487 typedef struct {
488 double x, y, r;
489 double start_pos;
490 double start_distance;
491 } MovePieData;
492
493 static gboolean
find_element(GogView * view,double cx,double cy,double x,double y,unsigned int * index,GogPieSeries ** series)494 find_element (GogView *view, double cx, double cy, double x, double y,
495 unsigned int *index, GogPieSeries **series)
496 {
497 GogPiePlot *pie = GOG_PIE_PLOT (view->model);
498 GSList *ptr;
499 double *vals, scale, len = 0, theta;
500
501 *series = NULL;
502 *index = 0;
503
504 for (ptr = pie->base.series ; ptr != NULL ; ptr = ptr->next)
505 if (gog_series_is_valid (GOG_SERIES (*series = ptr->data)))
506 break;
507 if (ptr == NULL)
508 return FALSE;
509
510 theta = (atan2 (y - cy, x - cx)
511 * 180 / M_PI - pie->initial_angle + 90.) / pie->span / 3.6;
512 if (theta < 0)
513 theta += 1.;
514
515 vals = go_data_get_values ((*series)->base.values[1].data);
516 scale = 1 / (*series)->total;
517 for (*index = 0 ; *index < (*series)->base.num_elements; (*index)++) {
518 len = vals[*index] * scale;
519 if (len < 0.)
520 len = pie->show_negatives? -len: 0.;
521 if (go_finite (len) && len > 1e-3) {
522 theta -= len;
523 if (theta < 0)
524 break;
525 }
526 }
527 return TRUE;
528 }
529
530 static gboolean
gog_tool_move_pie_point(GogView * view,double x,double y,GogObject ** gobj)531 gog_tool_move_pie_point (GogView *view, double x, double y, GogObject **gobj)
532 {
533 GogPieSeries *series;
534 double r = view->allocation.h, cx, cy;
535 unsigned int index;
536
537 if (r > view->allocation.w)
538 r = view->allocation.w;
539 r /= 2.;
540 cx = view->allocation.x + view->allocation.w/2.;
541 cy = view->allocation.y + view->allocation.h/2.;
542
543 if (hypot (x - cx, y - cy) > fabs (r))
544 return FALSE;
545
546 if (find_element (view, cx, cy, x, y, &index, &series))
547 *gobj = GOG_OBJECT (gog_series_get_element (GOG_SERIES (series), index));
548
549 return TRUE;
550 }
551
552 static void
gog_tool_move_pie_render(GogView * view)553 gog_tool_move_pie_render (GogView *view)
554 {
555 GogViewAllocation rectangle;
556 double r = view->allocation.h;
557
558 if (r > view->allocation.w)
559 r = view->allocation.w;
560
561 rectangle.x = view->allocation.x + (view->allocation.w - r) / 2.0;
562 rectangle.y = view->allocation.y + (view->allocation.h - r) / 2.0;
563 rectangle.w = r;
564 rectangle.h = r;
565
566 gog_renderer_draw_selection_rectangle (view->renderer, &rectangle);
567 }
568
569 static void
gog_tool_move_pie_init(GogToolAction * action)570 gog_tool_move_pie_init (GogToolAction *action)
571 {
572 GogPiePlot *pie = GOG_PIE_PLOT (action->view->model);
573 MovePieData *data = g_new0 (MovePieData, 1);
574 GogViewAllocation area = action->view->allocation;
575
576 data->r = area.h;
577 if (data->r > area.w)
578 data->r = area.w;
579 data->r /= 2.;
580 data->start_pos = (data->r * (0.5 + pie->default_separation)) / (1 + pie->default_separation);
581 data->x = area.x + area.w / 2.;
582 data->y = area.y + area.h / 2.;
583 data->start_distance = hypot (action->start_x - data->x, action->start_y - data->y);
584
585 action->data = data;
586 }
587
588 static void
gog_tool_move_pie_move(GogToolAction * action,double x,double y)589 gog_tool_move_pie_move (GogToolAction *action, double x, double y)
590 {
591 GogPiePlot *pie = GOG_PIE_PLOT (action->view->model);
592 MovePieData *data = action->data;
593 double pos, separation;
594
595 pos = data->start_pos -
596 ((x - action->start_x) * (data->x - action->start_x) +
597 (y - action->start_y) * (data->y - action->start_y)) /
598 data->start_distance;
599 separation = (pos - 0.5 * data->r) / (data->r - pos);
600
601 gog_pie_plot_set_default_separation (pie, separation);
602 }
603
604 static void
gog_tool_move_pie_double_click(GogToolAction * action)605 gog_tool_move_pie_double_click (GogToolAction *action)
606 {
607 MovePieData *data = action->data;
608 GogPieSeries *series;
609 GogObject *obj;
610 unsigned int index;
611
612 if (!find_element (action->view, data->x, data->y,
613 action->start_x, action->start_y,
614 &index, &series))
615 return;
616
617 obj = GOG_OBJECT (gog_series_get_element (GOG_SERIES (series), index));
618 if (obj == NULL) {
619 obj = g_object_new (gog_pie_series_element_get_type (),
620 "index", index, NULL);
621 gog_object_add_by_name (GOG_OBJECT (series), "Point", obj);
622 }
623 }
624
625 static GogTool gog_tool_move_pie = {
626 N_("Move pie"),
627 GDK_FLEUR,
628 gog_tool_move_pie_point,
629 gog_tool_move_pie_render,
630 gog_tool_move_pie_init,
631 gog_tool_move_pie_move,
632 gog_tool_move_pie_double_click,
633 NULL /*destroy*/
634 };
635 #endif
636
637 /*****************************************************************************/
638
639 typedef GogPlotView GogPieView;
640 typedef GogPlotViewClass GogPieViewClass;
641
642 static GogViewClass *pie_view_parent_klass;
643
644 static int
gog_pie_view_get_data_at_point(GogPlotView * view,double x,double y,GogSeries ** series)645 gog_pie_view_get_data_at_point (GogPlotView *view, double x, double y, GogSeries **series)
646 {
647 GogPiePlot const *model = GOG_PIE_PLOT (view->base.model);
648 GogPieSeries const *pseries = NULL;
649 double r_tot, r_cur, r_int, r_ext, cx, cy, r, th, th0, theta, scale, *vals;
650 double separated_cx, separated_cy;
651 unsigned int index, k;
652 double center_radius;
653 double center_size = 0.0;
654 unsigned num_series = 0;
655 double default_sep, len;
656 double separation_max = 0., separation;
657 GSList *ptr;
658 double outline_width_max = 0.;
659 gboolean has_hole;
660 GogPieSeriesElement *gpse;
661 GList const *overrides;
662 GogShowNegsMode mode = model->show_negatives;
663 gboolean negative;
664 GOStyle *style;
665
666 *series = NULL;
667 /* compute number of valid series */
668 for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
669 if (!gog_series_is_valid (GOG_SERIES (ptr->data)))
670 continue;
671 pseries = ptr->data;
672 if ((style = go_styled_object_get_style (GO_STYLED_OBJECT (pseries))))
673 outline_width_max = MAX (outline_width_max, gog_renderer_line_size (view->base.renderer, style->line.width));
674 for (overrides = gog_series_get_overrides (GOG_SERIES (pseries));
675 overrides != NULL;
676 overrides = overrides->next) {
677 separation = GOG_PIE_SERIES_ELEMENT (overrides->data)->separation;
678 if (separation_max < separation)
679 separation_max = separation;
680 style = go_styled_object_get_style (GO_STYLED_OBJECT (overrides->data));
681 if (outline_width_max < style->line.width)
682 outline_width_max = style->line.width;
683 }
684 num_series++;
685 }
686 if (separation_max < -model->default_separation)
687 separation_max = -model->default_separation;
688
689 if (num_series <= 0)
690 return -1;
691
692 if (GOG_IS_RING_PLOT (model))
693 center_size = GOG_RING_PLOT(model)->center_size;
694 else if (num_series > 1)
695 num_series = 1;
696 /* calculate things when span < 100. */
697 if (model->span < 100.) {
698 double xmin, xmax, ymin, ymax, ratio;
699 double begin, cur, end;
700 if (num_series == 1) {
701 pseries = model->base.series->data;
702 begin = (model->initial_angle + pseries->initial_angle) * M_PI / 180. - M_PI / 2.;
703 end = begin + model->span * M_PI / 50.;
704 } else {
705 /* WARNING: this code has not been checked */
706 ptr = model->base.series;
707 while (!gog_series_is_valid (GOG_SERIES (ptr->data)))
708 ptr = ptr->next;
709 pseries = ptr->data;
710 begin = end = pseries->initial_angle;
711 for (ptr = ptr->next; ptr != NULL; ptr = ptr->next) {
712 if (!gog_series_is_valid (GOG_SERIES (ptr->data)))
713 continue;
714 pseries = ptr->data;
715 cur = pseries->initial_angle;
716 if (cur < begin)
717 begin = cur;
718 if (cur > end)
719 end = cur;
720 }
721 begin = (model->initial_angle + begin) * M_PI / 180. - M_PI / 2.;
722 end = ((model->initial_angle + end) / 180. + model->span / 50.) * M_PI - M_PI / 2.;
723 }
724 cur = ceil (begin / M_PI * 2 - 1e-10) * M_PI / 2;
725 xmin = xmax = cos (begin);
726 ymin = ymax = sin (begin);
727 while (cur < end) {
728 cx = cos (cur);
729 cy = sin (cur);
730 if (cx > xmax)
731 xmax = cx;
732 if (cx < xmin)
733 xmin = cx;
734 if (cy > ymax)
735 ymax = cy;
736 if (cy < ymin)
737 ymin = cy;
738 cur += M_PI / 2;
739 }
740 cx = cos (end);
741 cy = sin (end);
742 if (cx > xmax)
743 xmax = cx;
744 if (cx < xmin)
745 xmin = cx;
746 if (cy > ymax)
747 ymax = cy;
748 if (cy < ymin)
749 ymin = cy;
750 /* we ensure that the center will be visible */
751 if (xmin > 0.)
752 xmin = 0.;
753 if (xmax < 0.)
754 xmax = 0.;
755 if (ymin > 0.)
756 ymin = 0.;
757 if (ymax < 0.)
758 ymax = 0.;
759 ratio = (ymax - ymin) / (xmax - xmin);
760 if (view->base.allocation.h > view->base.allocation.w * ratio) {
761 r_tot = view->base.allocation.w * MAX (xmax, -xmin) / (xmax - xmin);
762 cx = view->base.allocation.x - view->base.allocation.w * xmin / (xmax - xmin);
763 cy = view->base.allocation.y +
764 (view->base.allocation.h + view->base.allocation.w * ratio) / 2.
765 - view->base.allocation.w * ratio * ymax / (ymax - ymin);
766 } else {
767 r_tot = view->base.allocation.h * MAX (ymax, -ymin) / (ymax - ymin);
768 cx = view->base.allocation.x +
769 (view->base.allocation.w - view->base.allocation.h / ratio) / 2.
770 - view->base.allocation.h / ratio * xmin / (xmax - xmin);
771 cy = view->base.allocation.y - view->base.allocation.h * ymin / (ymax - ymin);
772 }
773 r_tot /= 1. + model->default_separation + separation_max;
774 } else {
775 /* centre things */
776 cx = view->base.allocation.x + view->base.allocation.w/2.;
777 cy = view->base.allocation.y + view->base.allocation.h/2.;
778
779 r_tot = view->base.allocation.h;
780 if (r_tot > view->base.allocation.w)
781 r_tot = view->base.allocation.w;
782 r_tot /= 2. * (1. + model->default_separation + separation_max);
783 }
784
785 r = hypot (x - cx, y - cy);
786
787 default_sep = r_tot * model->default_separation;
788 center_radius = r_tot * center_size;
789 r_cur = r_tot * (1. - center_size);
790
791 if (r < center_radius)
792 return -1;
793
794 index = 1;
795 th0 = (model->initial_angle + pseries->initial_angle) * M_PI / 180. - M_PI / 2.;
796 th = atan2 (y - cy, x - cx);
797 if (th < th0)
798 th += 2 * M_PI;
799
800 for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
801 pseries = ptr->data;
802
803 if (!gog_series_is_valid (GOG_SERIES (pseries)))
804 continue;
805 if (index > num_series) /* people snuck extra series into a pie */
806 break;
807
808 if (num_series == index)
809 r_cur -= outline_width_max / 2.0;
810 has_hole = center_radius > 0. || index > 1;
811 r_int = (has_hole)? (center_radius + r_cur * ((double) index - 1.0) / (double) num_series): 0.;
812 r_ext = center_radius + r_cur * (double) index / (double) num_series;
813
814 theta = th0;
815
816 scale = 2 * M_PI / 100 * model->span / pseries->total;
817 vals = go_data_get_values (pseries->base.values[1].data);
818
819 overrides = gog_series_get_overrides (GOG_SERIES (pseries));
820
821 for (k = 0 ; k < pseries->base.num_elements; k++) {
822 len = vals[k] * scale;
823 negative = len < 0;
824 if (negative) {
825 if (mode == GOG_SHOW_NEGS_SKIP) {
826 if ((overrides != NULL) &&
827 (GOG_SERIES_ELEMENT (overrides->data)->index == k))
828 overrides = overrides->next;
829 continue;
830 }
831 len = -len;
832 }
833 if (!go_finite (len) || len < 1e-3) {
834 if ((overrides != NULL) &&
835 (GOG_SERIES_ELEMENT (overrides->data)->index == k))
836 overrides = overrides->next;
837 continue;
838 }
839
840 gpse = NULL;
841 if ((overrides != NULL) &&
842 (GOG_SERIES_ELEMENT (overrides->data)->index == k)) {
843 gpse = GOG_PIE_SERIES_ELEMENT (overrides->data);
844 overrides = overrides->next;
845 }
846
847 /* only separate the outer ring */
848 separated_cx = cx;
849 separated_cy = cy;
850 if (num_series == index && (default_sep > 0. || gpse != NULL)) {
851
852 separation = default_sep;
853
854 if (gpse != NULL)
855 separation += gpse->separation * r_tot;
856
857 separated_cx += separation * cos (theta + len/2.);
858 separated_cy += separation * sin (theta + len/2.);
859 r = hypot (x - separated_cx, y - separated_cy);
860 th = atan2 (y - separated_cy, x - separated_cx);
861 if (th < th0)
862 th += 2 * M_PI;
863 }
864 theta += len;
865 if (r > r_int && r <= r_ext && th > theta - len && th <= theta) {
866 *series = GOG_SERIES (pseries);
867 return k;
868 }
869 if (gpse) {
870 r = hypot (x - cx, y - cy);
871 th = atan2 (y - cy, x - cx);
872 if (th < th0)
873 th += 2 * M_PI;
874 }
875 }
876 index ++;
877 }
878 return -1;
879 }
880
881 #define MAX_ARC_SEGMENTS 64
882
883 static void
gog_pie_view_render(GogView * view,GogViewAllocation const * bbox)884 gog_pie_view_render (GogView *view, GogViewAllocation const *bbox)
885 {
886 GogPiePlot const *model = GOG_PIE_PLOT (view->model);
887 GogPieSeries const *series = NULL;
888 double separated_cx, separated_cy, cx, cy, r, theta, len, *vals, scale;
889 double default_sep;
890 unsigned k;
891 GOPath *path;
892 GogTheme *theme = gog_object_get_theme (GOG_OBJECT (model));
893 GOStyle *style, *white_style = NULL, *elt_style = NULL;
894 GSList *ptr;
895 unsigned num_series = 0;
896 unsigned index;
897 double center_radius;
898 double center_size = 0.0;
899 double r_ext, r_int, r_tot;
900 double outline_width_max;
901 gboolean has_hole;
902 double separation_max, separation;
903 GogPieSeriesElement *gpse;
904 GList const *overrides;
905 GogShowNegsMode mode = model->show_negatives;
906 gboolean negative;
907
908 /* compute number of valid series */
909 for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
910 series = ptr->data;
911 if (!gog_series_is_valid (GOG_SERIES (ptr->data)))
912 continue;
913 num_series++;
914 }
915
916 if (num_series <= 0)
917 return;
918
919 separation_max = .0;
920 outline_width_max = .0;
921 if ((style = go_styled_object_get_style (GO_STYLED_OBJECT (series))))
922 outline_width_max = gog_renderer_line_size (view->renderer, style->line.width);
923 if (mode == GOG_SHOW_NEGS_WHITE) {
924 white_style = go_style_dup (style);
925 white_style->fill.type = GO_STYLE_FILL_PATTERN;
926 go_pattern_set_solid (&white_style->fill.pattern, GO_COLOR_WHITE);
927 }
928 for (overrides = gog_series_get_overrides (GOG_SERIES (series));
929 overrides != NULL;
930 overrides = overrides->next) {
931 separation = GOG_PIE_SERIES_ELEMENT (overrides->data)->separation;
932 if (separation_max < separation)
933 separation_max = separation;
934 style = go_styled_object_get_style (GO_STYLED_OBJECT (overrides->data));
935 if (outline_width_max < style->line.width)
936 outline_width_max = style->line.width;
937 }
938 if (separation_max < -model->default_separation)
939 separation_max = -model->default_separation;
940
941 if (GOG_IS_RING_PLOT (model))
942 center_size = GOG_RING_PLOT(model)->center_size;
943 else if (num_series > 1)
944 num_series = 1;
945
946 /* calculate things when span < 100. */
947 if (model->span < 100.) {
948 double xmin, xmax, ymin, ymax, ratio;
949 double begin, cur, end;
950 if (num_series == 1) {
951 begin = (model->initial_angle + series->initial_angle) * M_PI / 180. - M_PI / 2.;
952 end = begin + model->span * M_PI / 50.;
953 } else {
954 /* WARNING: this code has not been checked */
955 ptr = model->base.series;
956 while (!gog_series_is_valid (GOG_SERIES (ptr->data)))
957 ptr = ptr->next;
958 series = ptr->data;
959 begin = end = series->initial_angle;
960 for (ptr = ptr->next; ptr != NULL; ptr = ptr->next) {
961 if (!gog_series_is_valid (GOG_SERIES (ptr->data)))
962 continue;
963 series = ptr->data;
964 cur = series->initial_angle;
965 if (cur < begin)
966 begin = cur;
967 if (cur > end)
968 end = cur;
969 }
970 begin = (model->initial_angle + begin) * M_PI / 180. - M_PI / 2.;
971 end = ((model->initial_angle + end) / 180. + model->span / 50.) * M_PI - M_PI / 2.;
972 }
973 cur = ceil (begin / M_PI * 2 - 1e-10) * M_PI / 2;
974 xmin = xmax = cos (begin);
975 ymin = ymax = sin (begin);
976 while (cur < end) {
977 cx = cos (cur);
978 cy = sin (cur);
979 if (cx > xmax)
980 xmax = cx;
981 if (cx < xmin)
982 xmin = cx;
983 if (cy > ymax)
984 ymax = cy;
985 if (cy < ymin)
986 ymin = cy;
987 cur += M_PI / 2;
988 }
989 cx = cos (end);
990 cy = sin (end);
991 if (cx > xmax)
992 xmax = cx;
993 if (cx < xmin)
994 xmin = cx;
995 if (cy > ymax)
996 ymax = cy;
997 if (cy < ymin)
998 ymin = cy;
999 /* we ensure that the center will be visible */
1000 if (xmin > 0.)
1001 xmin = 0.;
1002 if (xmax < 0.)
1003 xmax = 0.;
1004 if (ymin > 0.)
1005 ymin = 0.;
1006 if (ymax < 0.)
1007 ymax = 0.;
1008 ratio = (ymax - ymin) / (xmax - xmin);
1009 if (view->allocation.h > view->allocation.w * ratio) {
1010 r_tot = view->allocation.w * MAX (xmax, -xmin) / (xmax - xmin);
1011 cx = view->allocation.x - view->allocation.w * xmin / (xmax - xmin);
1012 cy = view->allocation.y +
1013 (view->allocation.h + view->allocation.w * ratio) / 2.
1014 - view->allocation.w * ratio * ymax / (ymax - ymin);
1015 } else {
1016 r_tot = view->allocation.h * MAX (ymax, -ymin) / (ymax - ymin);
1017 cx = view->allocation.x +
1018 (view->allocation.w - view->allocation.h / ratio) / 2.
1019 - view->allocation.h / ratio * xmin / (xmax - xmin);
1020 cy = view->allocation.y - view->allocation.h * ymin / (ymax - ymin);
1021 }
1022 r_tot /= 1. + model->default_separation + separation_max;
1023 } else {
1024 /* centre things */
1025 cx = view->allocation.x + view->allocation.w/2.;
1026 cy = view->allocation.y + view->allocation.h/2.;
1027
1028 r_tot = view->allocation.h;
1029 if (r_tot > view->allocation.w)
1030 r_tot = view->allocation.w;
1031 r_tot /= 2. * (1. + model->default_separation + separation_max);
1032 }
1033
1034 path = go_path_new ();
1035
1036 default_sep = r_tot * model->default_separation;
1037 center_radius = r_tot * center_size;
1038 r = r_tot * (1. - center_size);
1039
1040 index = 1;
1041 for (ptr = model->base.series ; ptr != NULL ; ptr = ptr->next) {
1042 series = ptr->data;
1043
1044 if (!gog_series_is_valid (GOG_SERIES (series)))
1045 continue;
1046 if (index > num_series) /* people snuck extra series into a pie */
1047 break;
1048
1049 if (num_series == index)
1050 r -= outline_width_max / 2.0;
1051
1052 has_hole = center_radius > 0. || index > 1;
1053 r_int = center_radius + r * ((double)index - 1.0) / (double)num_series;
1054 r_ext = center_radius + r * (double)index / (double)num_series;
1055
1056 theta = (model->initial_angle + series->initial_angle) * M_PI / 180. - M_PI / 2.;
1057
1058 scale = 2 * M_PI / 100 * model->span / series->total;
1059 vals = go_data_get_values (series->base.values[1].data);
1060
1061 style = GOG_STYLED_OBJECT (series)->style;
1062 if (model->base.vary_style_by_element || mode == GOG_SHOW_NEGS_WHITE)
1063 style = go_style_dup (style);
1064 gog_renderer_push_style (view->renderer, style);
1065
1066 overrides = gog_series_get_overrides (GOG_SERIES (series));
1067 for (k = 0 ; k < series->base.num_elements; k++) {
1068 len = vals[k] * scale;
1069 negative = len < 0;
1070 if (negative) {
1071 if (mode == GOG_SHOW_NEGS_SKIP) {
1072 if ((overrides != NULL) &&
1073 (GOG_SERIES_ELEMENT (overrides->data)->index == k))
1074 overrides = overrides->next;
1075 continue;
1076 }
1077 len = -len;
1078 }
1079 if (!go_finite (len) || len < 1e-3) {
1080 if ((overrides != NULL) &&
1081 (GOG_SERIES_ELEMENT (overrides->data)->index == k))
1082 overrides = overrides->next;
1083 continue;
1084 }
1085
1086 gpse = NULL;
1087 if ((overrides != NULL) &&
1088 (GOG_SERIES_ELEMENT (overrides->data)->index == k)) {
1089 gpse = GOG_PIE_SERIES_ELEMENT (overrides->data);
1090 overrides = overrides->next;
1091 if (negative && mode == GOG_SHOW_NEGS_WHITE) {
1092 elt_style = go_style_dup (go_styled_object_get_style (
1093 GO_STYLED_OBJECT (gpse)));
1094 elt_style->fill.type = GO_STYLE_FILL_PATTERN;
1095 go_pattern_set_solid (&elt_style->fill.pattern, GO_COLOR_WHITE);
1096 gog_renderer_push_style (view->renderer, elt_style);
1097 } else
1098 gog_renderer_push_style (view->renderer,
1099 go_styled_object_get_style (
1100 GO_STYLED_OBJECT (gpse)));
1101 } else {
1102 if (negative && mode == GOG_SHOW_NEGS_WHITE)
1103 gog_renderer_push_style (view->renderer, white_style);
1104 else if (model->base.vary_style_by_element)
1105 gog_theme_fillin_style (theme, style, GOG_OBJECT (series),
1106 model->base.index_num + k, GO_STYLE_FILL);
1107 }
1108
1109 /* only separate the outer ring */
1110 separated_cx = cx;
1111 separated_cy = cy;
1112 if (num_series == index && (default_sep > 0. || gpse != NULL)) {
1113
1114 separation = default_sep;
1115
1116 if (gpse != NULL)
1117 separation += gpse->separation * r_tot;
1118
1119 separated_cx += separation * cos (theta + len/2.);
1120 separated_cy += separation * sin (theta + len/2.);
1121 }
1122 theta += len;
1123
1124 go_path_ring_wedge (path, separated_cx, separated_cy,
1125 r_ext, r_ext,
1126 has_hole ? r_int : 0.0,
1127 has_hole ? r_int : 0.0,
1128 theta - len,
1129 theta);
1130 gog_renderer_draw_shape (view->renderer, path);
1131 go_path_clear (path);
1132
1133 if (gpse != NULL || (negative && mode == GOG_SHOW_NEGS_WHITE)) {
1134 gog_renderer_pop_style (view->renderer);
1135 if (elt_style) {
1136 g_object_unref (elt_style);
1137 elt_style = NULL;
1138 }
1139 }
1140 }
1141
1142 gog_renderer_pop_style (view->renderer);
1143 if (model->base.vary_style_by_element || mode == GOG_SHOW_NEGS_WHITE)
1144 g_object_unref (style);
1145 if (white_style)
1146 g_object_unref (white_style);
1147
1148 index ++;
1149 }
1150
1151 go_path_free (path);
1152 }
1153
1154 static void
gog_pie_view_build_toolkit(GogView * view)1155 gog_pie_view_build_toolkit (GogView *view)
1156 {
1157 (GOG_VIEW_CLASS (pie_view_parent_klass)->build_toolkit) (view);
1158 #ifdef GOFFICE_WITH_GTK
1159 view->toolkit = g_slist_prepend (view->toolkit, &gog_tool_move_pie);
1160 #endif
1161 }
1162
1163 static char*
gog_pie_view_get_tip_at_point(GogView * view,double x,double y)1164 gog_pie_view_get_tip_at_point (GogView *view, double x, double y)
1165 {
1166 GogPieSeries *series = NULL;
1167 int index = gog_pie_view_get_data_at_point (GOG_PLOT_VIEW (view), x, y, (GogSeries** )&series);
1168 char *label, *ret;
1169 double *vals, value;
1170
1171 if (index < 0)
1172 return NULL;
1173 vals = go_data_get_values (series->base.values[1].data);
1174 value = fabs (vals[index]);
1175 label = series->base.values[0].data? go_data_get_vector_string (series->base.values[0].data, index): NULL;
1176 if (label && *label)
1177 ret = g_strdup_printf (_("%s: %g (%.2f%%)"), label, value, value * 100 / series->total);
1178 else
1179 ret = g_strdup_printf (_("%g (%.2f%%)"), value, value * 100 / series->total);
1180 g_free (label);
1181 return ret;
1182 }
1183
1184 static void
gog_pie_view_class_init(GogViewClass * view_klass)1185 gog_pie_view_class_init (GogViewClass *view_klass)
1186 {
1187 GogPlotViewClass *pv_klass = (GogPlotViewClass *) view_klass;
1188 pie_view_parent_klass = g_type_class_peek_parent (view_klass);
1189
1190 view_klass->render = gog_pie_view_render;
1191 view_klass->build_toolkit = gog_pie_view_build_toolkit;
1192 view_klass->get_tip_at_point = gog_pie_view_get_tip_at_point;
1193 pv_klass->get_data_at_point = gog_pie_view_get_data_at_point;
1194 }
1195
1196 GSF_DYNAMIC_CLASS (GogPieView, gog_pie_view,
1197 gog_pie_view_class_init, NULL,
1198 GOG_TYPE_PLOT_VIEW)
1199
1200 /*****************************************************************************/
1201
1202 typedef GogSeriesClass GogPieSeriesClass;
1203 enum {
1204 SERIES_PROP_0,
1205 SERIES_PROP_INITIAL_ANGLE,
1206 SERIES_PROP_SEPARATION,
1207 };
1208
1209 static GogObjectClass *series_parent_klass;
1210 static void
gog_pie_series_set_property(GObject * obj,guint param_id,GValue const * value,GParamSpec * pspec)1211 gog_pie_series_set_property (GObject *obj, guint param_id,
1212 GValue const *value, GParamSpec *pspec)
1213 {
1214 GogPieSeries *pie = GOG_PIE_SERIES (obj);
1215
1216 switch (param_id) {
1217 case SERIES_PROP_INITIAL_ANGLE:
1218 pie->initial_angle = g_value_get_double (value);
1219 break;
1220 case SERIES_PROP_SEPARATION:
1221 pie->separation = g_value_get_double (value);
1222 break;
1223 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1224 return; /* NOTE : RETURN */
1225 }
1226 /* none of the attributes triggers a size change yet.
1227 * When we add data labels we'll need it */
1228 gog_object_emit_changed (GOG_OBJECT (obj), FALSE);
1229 }
1230
1231 static void
gog_pie_series_get_property(GObject * obj,guint param_id,GValue * value,GParamSpec * pspec)1232 gog_pie_series_get_property (GObject *obj, guint param_id,
1233 GValue *value, GParamSpec *pspec)
1234 {
1235 GogPieSeries *pie = GOG_PIE_SERIES (obj);
1236
1237 switch (param_id) {
1238 case SERIES_PROP_INITIAL_ANGLE:
1239 g_value_set_double (value, pie->initial_angle);
1240 break;
1241 case SERIES_PROP_SEPARATION:
1242 g_value_set_double (value, pie->separation);
1243 break;
1244 default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
1245 break;
1246 }
1247 }
1248
1249 static void
gog_pie_series_update(GogObject * obj)1250 gog_pie_series_update (GogObject *obj)
1251 {
1252 double *vals = NULL, total;
1253 int len = 0;
1254 GogPieSeries *series = GOG_PIE_SERIES (obj);
1255 unsigned old_num = series->base.num_elements;
1256 GogShowNegsMode mode = GOG_PIE_PLOT (series->base.plot)->show_negatives;
1257
1258 if (series->base.values[1].data != NULL) {
1259 vals = go_data_get_values (series->base.values[1].data);
1260 len = go_data_get_vector_size (series->base.values[1].data);
1261 }
1262 series->base.num_elements = len;
1263
1264 for (total = 0. ; len-- > 0 ;) {
1265 double val = vals[len];
1266 if (go_finite (val)) {
1267 if (val < 0)
1268 val = (mode == GOG_SHOW_NEGS_SKIP)? 0.: -val;
1269 total += val;
1270 }
1271 }
1272 series->total = total;
1273
1274 /* queue plot for redraw */
1275 gog_object_request_update (GOG_OBJECT (series->base.plot));
1276 if (old_num != series->base.num_elements)
1277 gog_plot_request_cardinality_update (series->base.plot);
1278
1279 if (series_parent_klass->update)
1280 series_parent_klass->update (obj);
1281 }
1282
1283 static void
gog_pie_series_class_init(GObjectClass * gobject_klass)1284 gog_pie_series_class_init (GObjectClass *gobject_klass)
1285 {
1286 GogObjectClass *gog_klass = (GogObjectClass *)gobject_klass;
1287 GogSeriesClass *series_klass = (GogSeriesClass *)gobject_klass;
1288
1289 series_parent_klass = g_type_class_peek_parent (gobject_klass);
1290 gog_klass->update = gog_pie_series_update;
1291 series_klass->series_element_type = GOG_TYPE_PIE_SERIES_ELEMENT;
1292
1293 gobject_klass->set_property = gog_pie_series_set_property;
1294 gobject_klass->get_property = gog_pie_series_get_property;
1295
1296 g_object_class_install_property (gobject_klass, SERIES_PROP_INITIAL_ANGLE,
1297 g_param_spec_double ("initial-angle",
1298 _("Initial-angle"),
1299 _("Degrees clockwise from 12 O'Clock"),
1300 0, G_MAXFLOAT, 0.,
1301 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1302 g_object_class_install_property (gobject_klass, SERIES_PROP_SEPARATION,
1303 g_param_spec_double ("separation",
1304 _("Separation"),
1305 _("Default amount a slice is extended as a percentage of the radius"),
1306 0, G_MAXFLOAT, 0.,
1307 GSF_PARAM_STATIC | G_PARAM_READWRITE));
1308 }
1309
GSF_DYNAMIC_CLASS(GogPieSeries,gog_pie_series,gog_pie_series_class_init,NULL,GOG_TYPE_SERIES)1310 GSF_DYNAMIC_CLASS (GogPieSeries, gog_pie_series,
1311 gog_pie_series_class_init, NULL,
1312 GOG_TYPE_SERIES)
1313
1314 G_MODULE_EXPORT void
1315 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
1316 {
1317 GTypeModule *module = go_plugin_get_type_module (plugin);
1318 gog_pie_series_element_register_type (module);
1319 gog_pie_plot_register_type (module);
1320 gog_pie_view_register_type (module);
1321 gog_pie_series_register_type (module);
1322 gog_ring_plot_register_type (module);
1323
1324 register_embedded_stuff ();
1325 }
1326
1327 G_MODULE_EXPORT void
go_plugin_shutdown(GOPlugin * plugin,GOCmdContext * cc)1328 go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
1329 {
1330 unregister_embedded_stuff ();
1331 }
1332