GUI: Move .ui files from goffice resources to glib resources
[gnumeric.git] / src / sheet-object-graph.c
blob12eced1ebf47651323186e3a41ca046712b0e81f
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * sheet-object-graph.c: Wrapper for GNOME Office graphs in gnumeric
6 * Copyright (C) 2003-2005 Jody Goldberg (jody@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
21 * USA
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
25 #include "gnumeric.h"
26 #include "sheet-object-graph.h"
28 #include "gnm-pane-impl.h"
29 #include "sheet-control-gui.h"
30 #include "gui-util.h"
31 #include "gui-file.h"
32 #include "gnm-graph-window.h"
33 #include "style-color.h"
34 #include "sheet-object-impl.h"
35 #include "wbc-gtk.h"
36 #include "commands.h"
37 #include "application.h"
38 #include "sheet.h"
39 #include "print-info.h"
40 #include "workbook.h"
41 #include "workbook-view.h"
42 #include "gutils.h"
43 #include <graph.h>
45 #include <goffice/goffice.h>
46 #include <goffice/canvas/goc-graph.h>
48 #include <gsf/gsf-impl-utils.h>
49 #include <gsf/gsf-utils.h>
50 #include <gsf/gsf-output-stdio.h>
51 #include <gsf/gsf-libxml.h>
52 #include <gdk/gdkkeysyms.h>
53 #include <gtk/gtk.h>
54 #include <math.h>
55 #include <string.h>
57 static void
58 so_graph_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
60 GocItem *view = GOC_ITEM (GOC_GROUP (sov)->children->data);
61 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
63 if (visible) {
64 goc_item_set (GOC_ITEM (sov),
65 "x", MIN (coords [0], coords[2]) / scale,
66 "y", MIN (coords [3], coords[1]) / scale,
67 NULL);
68 goc_item_set (view,
69 "width", (fabs (coords [2] - coords [0]) + 1.) / scale,
70 "height", (fabs (coords [3] - coords [1]) + 1.) / scale,
71 NULL);
73 goc_item_show (view);
74 } else
75 goc_item_hide (view);
78 static void
79 so_graph_goc_view_class_init (SheetObjectViewClass *sov_klass)
81 sov_klass->set_bounds = so_graph_view_set_bounds;
84 typedef SheetObjectView SOGraphGocView;
85 typedef SheetObjectViewClass SOGraphGocViewClass;
87 static GSF_CLASS (SOGraphGocView, so_graph_goc_view,
88 so_graph_goc_view_class_init, NULL,
89 GNM_SO_VIEW_TYPE)
91 /****************************************************************************/
92 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-graph-key"
94 #define SHEET_OBJECT_GRAPH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_GRAPH_TYPE, SheetObjectGraphClass))
96 typedef struct {
97 SheetObject base;
98 GogGraph *graph;
99 GogRenderer *renderer;
100 gulong add_sig, remove_sig;
101 } SheetObjectGraph;
102 typedef SheetObjectClass SheetObjectGraphClass;
104 static GObjectClass *parent_klass;
106 static void
107 sog_data_set_sheet (G_GNUC_UNUSED SheetObjectGraph *sog,
108 GOData *data, Sheet *sheet)
110 gnm_go_data_set_sheet (data, sheet);
113 static void
114 sog_data_foreach_dep (SheetObject *so, GOData *data,
115 SheetObjectForeachDepFunc func, gpointer user)
117 gnm_go_data_foreach_dep (data, so, func, user);
120 static void
121 cb_graph_add_data (G_GNUC_UNUSED GogGraph *graph,
122 GOData *data, SheetObjectGraph *sog)
124 sog_data_set_sheet (sog, data, sog->base.sheet);
126 static void
127 cb_graph_remove_data (G_GNUC_UNUSED GogGraph *graph,
128 GOData *data, SheetObjectGraph *sog)
130 sog_data_set_sheet (sog, data, NULL);
133 static void
134 sog_datas_set_sheet (SheetObjectGraph *sog, Sheet *sheet)
136 GSList *ptr = gog_graph_get_data (sog->graph);
137 for (; ptr != NULL ; ptr = ptr->next)
138 sog_data_set_sheet (sog, ptr->data, sheet);
139 g_object_set (sog->graph, "document", ((sheet)? sheet->workbook: NULL), NULL);
142 static void
143 gnm_sog_finalize (GObject *obj)
145 SheetObjectGraph *sog = GNM_SO_GRAPH (obj);
147 if (sog->renderer != NULL) {
148 g_object_unref (sog->renderer);
149 sog->renderer = NULL;
151 if (sog->graph != NULL) {
152 g_object_unref (sog->graph);
153 sog->graph = NULL;
156 parent_klass->finalize (obj);
159 static void
160 cb_graph_size_changed (GocItem *item, GtkAllocation *allocation)
162 GogRenderer *rend;
163 GogGraph *graph;
164 SheetObject *so = sheet_object_view_get_so (GNM_SO_VIEW (item->parent));
165 GnmPrintInformation *pi = so->sheet->print_info;
166 double top, bottom, left, right, edge_to_below_header, edge_to_above_footer, w, h, x = 0., y = 0.;
167 w = print_info_get_paper_width (pi, GTK_UNIT_POINTS);
168 h = print_info_get_paper_height (pi, GTK_UNIT_POINTS);
169 print_info_get_margins (pi, &top, &bottom, &left, &right, &edge_to_below_header, &edge_to_above_footer);
170 w -= left + right;
171 h -= edge_to_above_footer + edge_to_below_header;
172 g_object_get (item, "renderer", &rend, NULL);
173 g_object_get (rend, "model", &graph, NULL);
174 gog_graph_set_size (graph, w, h);
175 if (w / allocation->width > h / allocation->height) {
176 h = allocation->width * h / w;
177 w = allocation->width;
178 y = (allocation->height - h) / 2.;
179 } else {
180 w = allocation->height * w / h;
181 h = allocation->height;
182 x = (allocation->width - w) / 2.;
184 goc_item_set (item, "x", x, "width", w, "y", y, "height", h, NULL);
185 g_object_unref (graph);
186 g_object_unref (rend);
189 static gboolean
190 cb_post_new_view (GocItem *item)
192 GtkAllocation alloc;
193 alloc.width = goc_canvas_get_width (item->canvas);
194 alloc.height = goc_canvas_get_height (item->canvas);
195 cb_graph_size_changed (item, &alloc);
196 return FALSE;
199 static SheetObjectView *
200 gnm_sog_new_view (SheetObject *so, SheetObjectViewContainer *container)
202 GnmPane *pane;
203 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
204 if (GNM_IS_PANE (container)) {
205 GocItem *view;
206 pane = GNM_PANE (container);
207 view = goc_item_new (pane->object_views,
208 so_graph_goc_view_get_type (),
209 NULL);
210 goc_item_new (GOC_GROUP (view),
211 GOC_TYPE_GRAPH,
212 "renderer", sog->renderer,
213 NULL);
214 return gnm_pane_object_register (so, view, TRUE);
215 } else {
216 GocCanvas *canvas = GOC_CANVAS (container);
217 GocItem *view = goc_item_new (goc_canvas_get_root (canvas),
218 so_graph_goc_view_get_type (),
219 NULL);
220 GocItem *item = goc_item_new (GOC_GROUP (view),
221 GOC_TYPE_GRAPH,
222 "renderer", sog->renderer,
223 NULL);
224 g_idle_add ((GSourceFunc) cb_post_new_view, item);
225 g_signal_connect_swapped (canvas, "size_allocate", G_CALLBACK (cb_graph_size_changed), item);
226 return (SheetObjectView *) view;
230 static GtkTargetList *
231 gnm_sog_get_target_list (G_GNUC_UNUSED SheetObject const *so)
233 GtkTargetList *tl = gtk_target_list_new (NULL, 0);
234 char *mime_str = go_image_format_to_mime ("svg");
235 GSList *mimes, *ptr;
237 mimes = go_strsplit_to_slist (mime_str, ',');
238 for (ptr = mimes; ptr != NULL; ptr = ptr->next) {
239 const char *mime = ptr->data;
241 if (mime != NULL && *mime != '\0')
242 gtk_target_list_add (tl, gdk_atom_intern (mime, FALSE),
243 0, 0);
245 g_free (mime_str);
246 g_slist_free_full (mimes, g_free);
247 /* No need to eliminate duplicates. */
248 gtk_target_list_add_image_targets (tl, 0, TRUE);
250 return tl;
253 static GtkTargetList *
254 gnm_sog_get_object_target_list (G_GNUC_UNUSED SheetObject const *so)
256 GtkTargetList *tl = gtk_target_list_new (NULL, 0);
257 gtk_target_list_add (tl,
258 gdk_atom_intern ("application/x-goffice-graph", FALSE), 0, 0);
259 return tl;
262 static void
263 gnm_sog_write_image (SheetObject const *so, char const *format, double resolution,
264 GsfOutput *output, GError **err)
266 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
267 gboolean res = FALSE;
268 double coords[4];
269 double w, h;
271 if (so->sheet) {
272 sheet_object_position_pts_get (GNM_SO (sog), coords);
273 w = fabs (coords[2] - coords[0]) + 1.;
274 h = fabs (coords[3] - coords[1]) + 1.;
275 } else {
276 w = GPOINTER_TO_UINT
277 (g_object_get_data (G_OBJECT (so), "pt-width-at-copy"));
278 h = GPOINTER_TO_UINT
279 (g_object_get_data (G_OBJECT (so), "pt-height-at-copy"));
282 g_return_if_fail (w > 0 && h > 0);
284 res = gog_graph_export_image (sog->graph, go_image_get_format_from_name (format),
285 output, resolution, resolution);
287 if (!res && err && *err == NULL)
288 *err = g_error_new (gsf_output_error_id (), 0,
289 _("Unknown failure while saving image"));
292 static void
293 gnm_sog_write_object (SheetObject const *so, char const *format,
294 GsfOutput *output, G_GNUC_UNUSED GError **err,
295 GnmConventions const *convs)
297 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
298 GsfXMLOut *xout;
299 GogObject *graph;
301 g_return_if_fail (strcmp (format, "application/x-goffice-graph") == 0);
303 graph = gog_object_dup (GOG_OBJECT (sog->graph),
304 NULL, gog_dataset_dup_to_simple);
305 xout = gsf_xml_out_new (output);
306 gog_object_write_xml_sax (GOG_OBJECT (graph), xout, (gpointer)convs);
307 g_object_unref (xout);
308 g_object_unref (graph);
311 static void
312 sog_cb_save_as (SheetObject *so, SheetControl *sc)
314 WBCGtk *wbcg;
315 char *uri;
316 GError *err = NULL;
317 GsfOutput *output;
318 GSList *l;
319 GOImageFormat selected_format;
320 GOImageFormatInfo const *format_info;
321 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
322 double resolution;
324 g_return_if_fail (sog != NULL);
326 l = gog_graph_get_supported_image_formats ();
327 g_return_if_fail (l != NULL);
328 selected_format = GPOINTER_TO_UINT (l->data);
330 #warning "This violates model gui barrier"
331 wbcg = scg_wbcg (GNM_SCG (sc));
332 uri = go_gui_get_image_save_info (wbcg_toplevel (wbcg), l, &selected_format, &resolution);
333 if (!uri)
334 goto out;
335 output = go_file_create (uri, &err);
336 if (!output)
337 goto out;
338 format_info = go_image_get_format_info (selected_format);
339 sheet_object_write_image (so, format_info->name, resolution, output, &err);
340 g_object_unref (output);
342 if (err != NULL)
343 go_cmd_context_error (GO_CMD_CONTEXT (wbcg), err);
345 out:
346 g_free (uri);
347 g_slist_free (l);
350 static void
351 sog_cb_open_in_new_window (SheetObject *so, SheetControl *sc)
353 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
354 SheetControlGUI *scg = GNM_SCG (sc);
355 WBCGtk *wbcg = scg_wbcg (scg);
356 GtkWidget *window;
357 double coords[4];
359 g_return_if_fail (sog != NULL);
361 scg_object_anchor_to_coords (scg, sheet_object_get_anchor (so), coords);
362 window = gnm_graph_window_new (sog->graph,
363 floor (fabs (coords[2] - coords[0]) + 0.5),
364 floor (fabs (coords[3] - coords[1]) + 0.5));
365 gtk_window_set_screen (GTK_WINDOW (window),
366 gtk_window_get_screen (wbcg_toplevel (wbcg)));
367 gtk_window_present (GTK_WINDOW (window));
368 g_signal_connect (window, "delete-event",
369 G_CALLBACK (gtk_widget_destroy),
370 NULL);
373 static void
374 sog_cb_copy_to_new_sheet (SheetObject *so, SheetControl *sc)
376 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
377 SheetControlGUI *scg = GNM_SCG (sc);
378 WorkbookControl *wbc = scg_wbc (scg);
379 Sheet *sheet = wb_control_cur_sheet (wbc);
380 GogGraph *graph = GOG_GRAPH (gog_object_dup (GOG_OBJECT (sog->graph), NULL, NULL));
381 WorkbookSheetState *old_state = workbook_sheet_state_new (wb_control_get_workbook (wbc));
382 Sheet *new_sheet = workbook_sheet_add_with_type (
383 wb_control_get_workbook (wbc),
384 GNM_SHEET_OBJECT, -1,
385 gnm_sheet_get_max_cols (sheet),
386 gnm_sheet_get_max_rows (sheet));
387 SheetObject *new_sog = sheet_object_graph_new (graph);
388 print_info_set_paper_orientation (new_sheet->print_info, GTK_PAGE_ORIENTATION_LANDSCAPE);
389 sheet_object_set_sheet (new_sog, new_sheet);
390 wb_view_sheet_focus (wb_control_view (wbc), new_sheet);
391 cmd_reorganize_sheets (wbc, old_state, sheet);
392 g_object_unref (graph);
393 g_object_unref (new_sog);
396 static void
397 gnm_sog_populate_menu (SheetObject *so, GPtrArray *actions)
399 static SheetObjectAction const sog_actions[] = {
400 { "document-save-as", N_("_Save As Image"), NULL, 0, sog_cb_save_as, NULL },
401 { NULL, N_("Open in _New Window"), NULL, 0, sog_cb_open_in_new_window, NULL },
402 { NULL, N_("Copy to New Graph S_heet"), NULL, 0, sog_cb_copy_to_new_sheet, NULL }
405 unsigned int i;
407 GNM_SO_CLASS (parent_klass)->populate_menu (so, actions);
409 for (i = 0; i < G_N_ELEMENTS (sog_actions); i++)
410 g_ptr_array_insert (actions, 1 + i, (gpointer) (sog_actions + i));
413 static void
414 gnm_sog_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
415 GnmConventions const *convs)
417 SheetObjectGraph const *sog = GNM_SO_GRAPH (so);
418 gog_object_write_xml_sax (GOG_OBJECT (sog->graph), output,
419 (gpointer)convs);
422 static void
423 sog_xml_finish (GogObject *graph, SheetObject *so)
425 sheet_object_graph_set_gog (so, GOG_GRAPH (graph));
426 g_object_unref (graph);
429 static void gnm_sogg_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
430 xmlChar const **attrs,
431 GnmConventions const *convs);
432 static void
433 gnm_sog_prep_sax_parser (SheetObject *so, GsfXMLIn *xin, xmlChar const **attrs,
434 GnmConventions const *convs)
436 if (strcmp (xin->node->name, "GnmGraph"))
437 gog_object_sax_push_parser (xin, attrs,
438 (GogObjectSaxHandler) sog_xml_finish,
439 (gpointer)convs, so);
440 else
441 gnm_sogg_prep_sax_parser (so, xin, attrs, convs);
444 static void
445 gnm_sog_copy (SheetObject *dst, SheetObject const *src)
447 SheetObjectGraph const *sog = GNM_SO_GRAPH (src);
448 GogGraph *graph = gog_graph_dup (sog->graph);
449 sheet_object_graph_set_gog (dst, graph);
450 g_object_unref (graph);
453 static void
454 gnm_sog_draw_cairo (SheetObject const *so, cairo_t *cr,
455 double width, double height)
457 gog_graph_render_to_cairo (GNM_SO_GRAPH (so)->graph,
458 cr, width, height);
461 typedef struct {
462 SheetObject *so;
463 WorkbookControl *wbc;
464 } gnm_sog_user_config_t;
466 static void
467 gnm_sog_user_config_free_data (gpointer data,
468 GClosure *closure)
470 g_free (data);
471 closure->data = NULL;
474 static void
475 cb_update_graph (GogGraph *graph, gnm_sog_user_config_t *data)
477 cmd_so_graph_config (data->wbc, data->so, G_OBJECT (graph),
478 G_OBJECT (GNM_SO_GRAPH (data->so)->graph));
481 static void
482 gnm_sog_user_config (SheetObject *so, SheetControl *sc)
484 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
485 WBCGtk *wbcg;
486 gnm_sog_user_config_t *data;
487 GClosure *closure;
489 g_return_if_fail (sog != NULL);
490 g_return_if_fail (sc != NULL);
492 wbcg = scg_wbcg (GNM_SCG (sc));
494 data = g_new0 (gnm_sog_user_config_t, 1);
495 data->so = so;
496 data->wbc = GNM_WBC (wbcg);
498 closure = g_cclosure_new (G_CALLBACK (cb_update_graph), data,
499 (GClosureNotify) gnm_sog_user_config_free_data);
501 sheet_object_graph_guru (wbcg, sog->graph, closure);
502 g_closure_sink (closure);
505 static void
506 gnm_sog_foreach_dep (SheetObject *so,
507 SheetObjectForeachDepFunc func,
508 gpointer user)
510 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
511 GSList *ptr = gog_graph_get_data (sog->graph);
512 for (; ptr != NULL ; ptr = ptr->next)
513 sog_data_foreach_dep (so, ptr->data, func, user);
516 static gboolean
517 gnm_sog_set_sheet (SheetObject *so, Sheet *sheet)
519 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
520 if (sog->graph != NULL)
521 sog_datas_set_sheet (sog, sheet);
522 return FALSE;
525 static gboolean
526 gnm_sog_remove_from_sheet (SheetObject *so)
528 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
529 if (sog->graph != NULL)
530 sog_datas_set_sheet (sog, NULL);
531 return FALSE;
534 static void
535 gnm_sog_default_size (G_GNUC_UNUSED SheetObject const *so, double *w, double *h)
537 *w = GO_CM_TO_PT ((double)12);
538 *h = GO_CM_TO_PT ((double)8);
541 static void
542 gnm_sog_bounds_changed (SheetObject *so)
544 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
546 /* If it has not been realized there is no renderer yet */
547 if (sog->renderer != NULL) {
548 double coords [4];
549 if (so->sheet->sheet_type == GNM_SHEET_DATA) {
550 sheet_object_position_pts_get (so, coords);
551 gog_graph_set_size (sog->graph, fabs (coords[2] - coords[0]),
552 fabs (coords[3] - coords[1]));
553 } else {
554 /*FIXME: get dimensions from print settings */
555 gog_graph_set_size (sog->graph, 400., 300.);
560 static void
561 gnm_sog_class_init (GObjectClass *klass)
563 SheetObjectClass *so_class = GNM_SO_CLASS (klass);
565 parent_klass = g_type_class_peek_parent (klass);
567 /* Object class method overrides */
568 klass->finalize = gnm_sog_finalize;
570 /* SheetObject class method overrides */
571 so_class->new_view = gnm_sog_new_view;
572 so_class->bounds_changed = gnm_sog_bounds_changed;
573 so_class->populate_menu = gnm_sog_populate_menu;
574 so_class->write_xml_sax = gnm_sog_write_xml_sax;
575 so_class->prep_sax_parser = gnm_sog_prep_sax_parser;
576 so_class->copy = gnm_sog_copy;
577 so_class->user_config = gnm_sog_user_config;
578 so_class->assign_to_sheet = gnm_sog_set_sheet;
579 so_class->remove_from_sheet = gnm_sog_remove_from_sheet;
580 so_class->default_size = gnm_sog_default_size;
581 so_class->draw_cairo = gnm_sog_draw_cairo;
582 so_class->foreach_dep = gnm_sog_foreach_dep;
584 so_class->rubber_band_directly = FALSE;
587 static void
588 gnm_sog_init (GObject *obj)
590 SheetObject *so = GNM_SO (obj);
591 so->anchor.base.direction = GOD_ANCHOR_DIR_DOWN_RIGHT;
594 static void
595 sog_imageable_init (SheetObjectImageableIface *soi_iface)
597 soi_iface->get_target_list = gnm_sog_get_target_list;
598 soi_iface->write_image = gnm_sog_write_image;
601 static void
602 sog_exportable_init (SheetObjectExportableIface *soe_iface)
604 soe_iface->get_target_list = gnm_sog_get_object_target_list;
605 soe_iface->write_object = gnm_sog_write_object;
608 GSF_CLASS_FULL (SheetObjectGraph, sheet_object_graph,
609 NULL, NULL, gnm_sog_class_init,NULL,
610 gnm_sog_init, GNM_SO_TYPE, 0,
611 GSF_INTERFACE (sog_imageable_init, GNM_SO_IMAGEABLE_TYPE) \
612 GSF_INTERFACE (sog_exportable_init, GNM_SO_EXPORTABLE_TYPE))
615 * sheet_object_graph_new:
616 * @graph: (allow-none): #GogGraph
618 * Adds a reference to @graph and creates a gnumeric sheet object wrapper
619 * Returns: (transfer full): the newly allocated #SheetObject.
621 SheetObject *
622 sheet_object_graph_new (GogGraph *graph)
624 SheetObject *sog = g_object_new (GNM_SO_GRAPH_TYPE, NULL);
625 GnmGraphDataClosure *data = graph
626 ? (GnmGraphDataClosure *) g_object_get_data (G_OBJECT (graph), "data-closure")
627 : NULL;
628 sheet_object_graph_set_gog (sog, graph);
629 if (data != NULL)
630 sog->anchor.mode = data->anchor_mode;
632 return sog;
636 * sheet_object_graph_get_gog:
637 * @sog: #SheetObject
639 * Returns: (transfer none): the embedded #GogGraph or %NULL on error.
642 GogGraph *
643 sheet_object_graph_get_gog (SheetObject *sog)
645 g_return_val_if_fail (GNM_IS_SO_GRAPH (sog), NULL);
647 return ((SheetObjectGraph *)sog)->graph;
651 * sheet_object_graph_set_gog:
652 * @sog: #SheetObjectGraph
653 * @graph: (allow-none): #GogGraph
655 * If @graph is non NULL add a reference to it, otherwise create a new graph.
656 * Assign the graph to its SheetObjectGraph wrapper and initialize the
657 * handlers, disconnecting any existing connection for the preceding graph.
659 void
660 sheet_object_graph_set_gog (SheetObject *so, GogGraph *graph)
662 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
664 g_return_if_fail (GNM_IS_SO_GRAPH (so));
666 if (graph != NULL) {
667 if (sog->graph == graph)
668 return;
670 g_object_ref (graph);
671 } else
672 graph = g_object_new (GOG_TYPE_GRAPH, NULL);
674 if (sog->graph != NULL) {
675 g_signal_handler_disconnect (sog->graph, sog->add_sig);
676 g_signal_handler_disconnect (sog->graph, sog->remove_sig);
677 if (so->sheet != NULL)
678 sog_datas_set_sheet (sog, NULL);
679 g_object_unref (sog->graph);
682 sog->graph = graph;
683 if (so->sheet != NULL)
684 sog_datas_set_sheet (sog, so->sheet);
685 sog->add_sig = g_signal_connect_object (G_OBJECT (graph),
686 "add_data",
687 G_CALLBACK (cb_graph_add_data), G_OBJECT (sog), 0);
688 sog->remove_sig = g_signal_connect_object (G_OBJECT (graph),
689 "remove_data",
690 G_CALLBACK (cb_graph_remove_data), G_OBJECT (sog), 0);
692 if (sog->renderer != NULL)
693 g_object_set (sog->renderer, "model", graph, NULL);
694 else
695 sog->renderer = gog_renderer_new (sog->graph);
698 static void
699 cb_graph_guru_done (WBCGtk *wbcg)
701 wbcg_edit_finish (wbcg, WBC_EDIT_REJECT, NULL);
704 static void
705 cb_graph_data_closure_done (GnmGraphDataClosure *data)
707 if (data->obj)
708 g_object_set_data (data->obj,"data-closure", NULL);
709 g_free (data);
712 static void
713 cb_selection_mode_changed (GtkComboBox *box, GnmGraphDataClosure *data)
715 GogObject *graph = (GogObject *) g_object_get_data (data->obj, "graph");
716 data->colrowmode = gtk_combo_box_get_active (box);
717 if (graph) {
718 GogObject *gobj = gog_object_get_child_by_name (graph, "Chart");
719 gobj = gog_object_get_child_by_name (gobj, "Plot");
720 if (!gobj)
721 return;
722 gog_plot_clear_series (GOG_PLOT (gobj));
723 gog_data_allocator_allocate (data->dalloc, GOG_PLOT (gobj));
727 static void
728 cb_shared_mode_changed (GtkToggleButton *btn, GnmGraphDataClosure *data)
730 GogObject *graph = (GogObject *) g_object_get_data (data->obj, "graph");
731 data->share_x = gtk_toggle_button_get_active (btn);
732 if (graph) {
733 GogObject *gobj = gog_object_get_child_by_name (graph, "Chart");
734 gobj = gog_object_get_child_by_name (gobj, "Plot");
735 if (!gobj)
736 return;
737 gog_plot_clear_series (GOG_PLOT (gobj));
738 gog_data_allocator_allocate (data->dalloc, GOG_PLOT (gobj));
742 static void
743 cb_sheet_target_changed (GtkToggleButton *btn, GnmGraphDataClosure *data)
745 data->new_sheet = gtk_toggle_button_get_active (btn);
748 void
749 sheet_object_graph_guru (WBCGtk *wbcg, GogGraph *graph,
750 GClosure *closure)
752 GtkWidget *dialog = gog_guru (graph, GOG_DATA_ALLOCATOR (wbcg),
753 GO_CMD_CONTEXT (wbcg), closure);
754 if (!graph) {
755 GnmGraphDataClosure *data = (GnmGraphDataClosure *) g_new0 (GnmGraphDataClosure, 1);
756 GtkWidget *custom = gtk_grid_new (), *w;
757 GObject *object;
759 data->dalloc = GOG_DATA_ALLOCATOR (wbcg);
760 g_object_set (custom,
761 "row-spacing", 6,
762 "column-spacing", 12,
763 "margin-top", 6,
764 NULL);
765 w = gtk_label_new (_("Series as:"));
766 g_object_set (w, "xalign", 0., NULL);
767 gtk_grid_attach (GTK_GRID (custom), w, 0, 0, 1, 1);
768 w = gtk_combo_box_text_new ();
769 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Auto"));
770 /*Translators: Series as "Columns"*/
771 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), C_("graph", "Columns"));
772 /*Translators: Series as "Rows"*/
773 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), C_("graph", "Rows"));
774 gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
775 g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_selection_mode_changed), data);
776 gtk_grid_attach (GTK_GRID (custom), w, 1, 0, 1, 1);
777 w = gtk_check_button_new_with_label (_("Use first series as shared abscissa"));
778 g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (cb_shared_mode_changed), data);
779 gtk_grid_attach (GTK_GRID (custom), w, 0, 1, 2, 1);
780 w = gtk_check_button_new_with_label (_("New graph sheet"));
781 g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (cb_sheet_target_changed), data);
782 gtk_grid_attach (GTK_GRID (custom), w, 0, 2, 2, 1);
783 data->obj = G_OBJECT (custom);
784 data->anchor_mode = GNM_SO_ANCHOR_ONE_CELL; /* don't resize graphs with cells, see # */
785 gog_guru_add_custom_widget (dialog, custom);
786 object = (GObject*) g_object_get_data (data->obj, "graph");
787 if (object)
788 g_object_set_data (object, "data-closure", data);
789 g_object_set_data_full (G_OBJECT (custom), "data-closure", data, (GDestroyNotify) cb_graph_data_closure_done);
791 gnm_init_help_button (
792 gog_guru_get_help_button (dialog),
793 "chapter-graphs");
794 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog),
795 wbcg, GNM_DIALOG_DESTROY_SHEET_REMOVED);
796 gnm_keyed_dialog (wbcg, GTK_WINDOW (dialog), "graph-guru");
797 g_object_set_data_full (G_OBJECT (dialog),
798 "guru", wbcg, (GDestroyNotify) cb_graph_guru_done);
799 wbc_gtk_attach_guru (wbcg, dialog);
800 gtk_widget_show (dialog);
804 * sheet_object_graph_ensure_size:
805 * @sog: #SheetObject
807 * Updates the size of the graph item in the canvas for graph sheets objects.
809 void
810 sheet_object_graph_ensure_size (SheetObject *sog)
812 GList *ptr = sog->realized_list;
813 while (ptr) {
814 cb_post_new_view (GOC_ITEM (GOC_GROUP (ptr->data)->children->data));
815 ptr = ptr->next;
819 /*****************************************************************************/
820 /* Support for Guppi graphs */
822 typedef struct {
823 GnmConventions const *convs;
824 GogGraph *graph;
825 GogObject *chart;
826 GogPlot *plot;
827 GogObject *cur;
828 GogStyle *style;
829 GPtrArray *data;
830 unsigned cur_index, max_data;
831 } GuppiReadState;
833 static void
834 vector_start (GsfXMLIn *xin, xmlChar const **attrs)
836 GuppiReadState *state = (GuppiReadState *) xin->user_state;
837 int i;
838 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
839 if (0 == strcmp (attrs[i], "ID"))
840 state->cur_index = strtoul (attrs[i+1], NULL, 10);
841 if (state->cur_index < 256 && state->cur_index >= state->max_data) {
842 state->max_data += 10;
843 g_ptr_array_set_size (state->data, state->max_data);
847 static void
848 vector_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
850 GuppiReadState *state = (GuppiReadState *) xin->user_state;
851 GOData *data;
852 if (state->cur_index >= state->max_data)
853 return;
854 data = g_object_new (GNM_GO_DATA_VECTOR_TYPE, NULL);
855 go_data_unserialize (data, xin->content->str, (void*) state->convs);
856 g_ptr_array_index (state->data, state->cur_index) = data;
859 static void
860 plot_type_start (GsfXMLIn *xin, xmlChar const **attrs)
862 GuppiReadState *state = (GuppiReadState *) xin->user_state;
863 int i;
864 char const *name = NULL;
865 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
866 if (0 == strcmp (attrs[i], "name"))
867 name = attrs[i+1];
868 if (name) {
869 if (0 == strcmp (name, "Scatter")) {
870 state->plot = gog_plot_new_by_name ("GogXYPlot");
871 g_object_set (G_OBJECT (state->plot),
872 "default-style-has-markers", FALSE,
873 "default-style-has-lines", FALSE,
874 NULL);
875 gog_object_add_by_name (state->chart, "Backplane", NULL);
877 else if (0 == strcmp (name, "Pie"))
878 state->plot = gog_plot_new_by_name ("GogPiePlot");
879 else if (0 == strcmp (name, "Bar")) {
880 state->plot = gog_plot_new_by_name ("GogBarColPlot");
881 gog_object_add_by_name (state->chart, "Backplane", NULL);
882 } else if (0 == strcmp (name, "Line")) {
883 state->plot = gog_plot_new_by_name ("GogLinePlot");
884 g_object_set (G_OBJECT (state->plot),
885 "default-style-has-markers", FALSE,
886 NULL);
887 gog_object_add_by_name (state->chart, "Backplane", NULL);
889 if (state->plot)
890 gog_object_add_by_name (GOG_OBJECT (state->chart), "Plot", GOG_OBJECT (state->plot));
894 static void
895 legend_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
897 GuppiReadState *state = (GuppiReadState *) xin->user_state;
898 state->cur = gog_object_add_by_name (state->chart, "Legend", NULL);
901 static void
902 position_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
904 GuppiReadState *state = (GuppiReadState *)xin->user_state;
905 if (!xin->content->str)
906 return;
907 if (0 == strcmp (xin->content->str, "east"))
908 g_object_set (G_OBJECT (state->cur), "compass", "right", NULL);
909 if (0 == strcmp (xin->content->str, "west"))
910 g_object_set (G_OBJECT (state->cur), "compass", "left", NULL);
911 if (0 == strcmp (xin->content->str, "north"))
912 g_object_set (G_OBJECT (state->cur), "compass", "top", NULL);
913 if (0 == strcmp (xin->content->str, "south"))
914 g_object_set (G_OBJECT (state->cur), "compass", "bottom", NULL);
917 static void
918 series_start (GsfXMLIn *xin, xmlChar const **attrs)
920 GuppiReadState *state = (GuppiReadState *) xin->user_state;
921 int i;
922 char *name = NULL;
923 GError *err = NULL;
924 GOData *data;
926 state->cur = GOG_OBJECT (gog_plot_new_series (state->plot));
927 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
928 if (0 == strcmp (attrs[i], "name"))
929 name = g_strdup_printf ("\"%s\"", attrs[i+1]);
930 if (name) {
931 data = g_object_new (GNM_GO_DATA_SCALAR_TYPE, NULL);
932 go_data_unserialize (data, name, (void*) state->convs);
933 gog_dataset_set_dim (GOG_DATASET (state->cur), -1,
934 data, &err);
935 g_free (name);
937 if (err)
938 g_error_free (err);
941 static void
942 dim_start (GsfXMLIn *xin, xmlChar const **attrs)
944 GuppiReadState *state = (GuppiReadState *) xin->user_state;
945 unsigned i, id = 0;
946 char const *name = "?";
947 GogMSDimType type = GOG_MS_DIM_LABELS;
948 GogPlotDesc const *desc = gog_plot_description (state->plot);
949 GError *err = NULL;
950 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
951 if (0 == strcmp (attrs[i], "dim_name"))
952 name = attrs[i+1];
953 else if (0 == strcmp (attrs[i], "ID"))
954 id = strtoul (attrs[i+1], NULL, 10);
955 if (!desc ||
956 id >= state->data->len || g_ptr_array_index (state->data, id) == NULL)
957 return;
958 if (0 == strcmp (name, "values"))
959 type = GOG_MS_DIM_VALUES;
960 else if (0 == strcmp (name, "categories"))
961 type = GOG_MS_DIM_CATEGORIES;
962 else if (0 == strcmp (name, "bubbles"))
963 type = GOG_MS_DIM_BUBBLES;
964 for (i = 0; i < desc->series.num_dim; i++)
965 if (desc->series.dim[i].ms_type == type) {
966 GOData *data = g_object_ref (g_ptr_array_index (state->data, id));
967 gog_dataset_set_dim (GOG_DATASET (state->cur), i,data,
968 &err);
969 break;
971 if (err)
972 g_error_free (err);
975 static void
976 marker_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
978 GuppiReadState *state = (GuppiReadState *)xin->user_state;
979 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
980 g_object_set (G_OBJECT (state->plot), "default-style-has-markers", TRUE, NULL);
983 static void
984 linear_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
986 GuppiReadState *state = (GuppiReadState *)xin->user_state;
987 g_object_set (G_OBJECT (state->plot),
988 "default-style-has-lines", TRUE,
989 NULL);
992 static void
993 cubic_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
995 GuppiReadState *state = (GuppiReadState *)xin->user_state;
996 g_object_set (G_OBJECT (state->plot),
997 "default-style-has-lines", TRUE,
998 "use-splines", TRUE,
999 NULL);
1002 static void
1003 horiz_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1005 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1006 if (xin->content->str)
1007 g_object_set (G_OBJECT (state->plot), "horizontal", 0 == strcmp (xin->content->str, "true"), NULL);
1010 static void
1011 stacked_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1013 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1014 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
1015 g_object_set (G_OBJECT (state->plot), "type", "stacked", NULL);
1018 static void
1019 percent_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1021 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1022 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
1023 g_object_set (G_OBJECT (state->plot), "type", "as_percentage", NULL);
1026 static void
1027 separation_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1029 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1030 if (xin->content->str) {
1031 double separation = g_ascii_strtod (xin->content->str, NULL);
1032 g_object_set (G_OBJECT (state->plot),
1033 "default-separation", separation / 100.,
1034 NULL);
1038 static void
1039 bubble_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1041 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1042 if (xin->content->str && 0 == strcmp (xin->content->str, "true")) {
1043 g_object_unref (state->plot);
1044 state->plot = gog_plot_new_by_name ("GogBubblePlot");
1045 gog_object_add_by_name (state->chart, "Backplane", NULL);
1049 static void
1050 gnm_sogg_sax_parser_done (G_GNUC_UNUSED GsfXMLIn *xin, GuppiReadState *state)
1052 unsigned i;
1053 GObject *obj;
1054 g_object_unref (state->graph);
1055 for (i = 0; i < state->max_data; i++) {
1056 obj = (GObject *) g_ptr_array_index (state->data, i);
1057 if (obj)
1058 g_object_unref (obj);
1060 g_ptr_array_free (state->data, TRUE);
1061 g_free (state);
1064 static void
1065 gnm_sogg_prep_sax_parser (SheetObject *so, GsfXMLIn *xin, xmlChar const **attrs,
1066 GnmConventions const *convs)
1068 static GsfXMLInNode const dtd[] = {
1069 GSF_XML_IN_NODE (GRAPH, GRAPH, -1, "GmrGraph", GSF_XML_NO_CONTENT, NULL, NULL),
1070 GSF_XML_IN_NODE (GRAPH, GUPPI_VECTORS, -1, "gmr:Vectors", GSF_XML_NO_CONTENT, NULL, NULL),
1071 GSF_XML_IN_NODE (GUPPI_VECTORS, GUPPI_VECTOR, -1, "gmr:Vector", GSF_XML_CONTENT, vector_start, vector_end),
1072 GSF_XML_IN_NODE (GRAPH, GUPPI_GRAPH, -1, "graph:Graph", GSF_XML_NO_CONTENT, NULL, NULL),
1073 GSF_XML_IN_NODE (GUPPI_GRAPH, GUPPI_LEGEND, -1, "graph:Legend", GSF_XML_NO_CONTENT, legend_start, NULL),
1074 GSF_XML_IN_NODE (GUPPI_LEGEND, GUPPI_LEGEND_POS, -1, "graph:Position", GSF_XML_CONTENT, NULL, position_end),
1075 GSF_XML_IN_NODE (GUPPI_GRAPH, GUPPI_PLOTS, -1, "graph:Plots", GSF_XML_NO_CONTENT, NULL, NULL),
1076 GSF_XML_IN_NODE (GUPPI_PLOTS, GUPPI_PLOT, -1, "graph:Plot", GSF_XML_NO_CONTENT, NULL, NULL),
1077 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_PLOT_TYPE, -1, "Type", GSF_XML_NO_CONTENT, plot_type_start, NULL),
1078 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_MARKER, -1, "with_marker", GSF_XML_CONTENT, NULL, marker_end),
1079 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_MARKERS, -1, "with_markers", GSF_XML_CONTENT, NULL, marker_end),
1080 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_LINES, -1, "with_line", GSF_XML_NO_CONTENT, NULL, NULL),
1081 GSF_XML_IN_NODE (GUPPI_LINES, GUPPI_PLOT_LINEAR, -1, "Linear", GSF_XML_NO_CONTENT, linear_start, NULL),
1082 GSF_XML_IN_NODE (GUPPI_LINES, GUPPI_PLOT_CUBIC, -1, "Cubic", GSF_XML_NO_CONTENT, cubic_start, NULL),
1083 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_HORIZONTAL, -1, "horizontal", GSF_XML_CONTENT, NULL, horiz_end),
1084 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_STACKED, -1, "stacked", GSF_XML_CONTENT, NULL, stacked_end),
1085 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_AS_PERCENT, -1, "as_percentage", GSF_XML_CONTENT, NULL, percent_end),
1086 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_SEPARATION, -1, "separation_percent_of_radius", GSF_XML_CONTENT, NULL, separation_end),
1087 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_BUBBLE, -1, "auto_allocate_bubble_size", GSF_XML_CONTENT, NULL, bubble_end),
1088 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_PLOT_GRAPH_TYPE, -1, "graph:Type", GSF_XML_NO_CONTENT, plot_type_start, NULL),
1089 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_MARKER, -1, "with_marker", GSF_XML_2ND, NULL, NULL),
1090 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_MARKERS, -1, "with_markers", GSF_XML_2ND, NULL, NULL),
1091 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_LINES, -1, "with_line", GSF_XML_2ND, NULL, NULL),
1092 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_HORIZONTAL, -1, "horizontal", GSF_XML_2ND, NULL, NULL),
1093 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_STACKED, -1, "stacked", GSF_XML_2ND, NULL, NULL),
1094 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_AS_PERCENT, -1, "as_percentage", GSF_XML_2ND, NULL, NULL),
1095 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_SEPARATION, -1, "separation_percent_of_radius", GSF_XML_2ND, NULL, NULL),
1096 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_BUBBLE, -1, "auto_allocate_bubble_size", GSF_XML_2ND, NULL, NULL),
1097 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_DATA, -1, "graph:Data", GSF_XML_NO_CONTENT, NULL, NULL),
1098 GSF_XML_IN_NODE (GUPPI_DATA, GUPPI_SERIES, -1, "graph:Series", GSF_XML_NO_CONTENT, series_start, NULL),
1099 GSF_XML_IN_NODE (GUPPI_SERIES, GUPPI_SERIES_DIM, -1, "graph:Dimension", GSF_XML_NO_CONTENT, dim_start, NULL),
1100 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_DATA_LAYOUT, -1, "graph:DataLayout", GSF_XML_NO_CONTENT, NULL, NULL),
1101 GSF_XML_IN_NODE (GUPPI_DATA_LAYOUT, GUPPI_DIMENSION, -1, "graph:Dimension", GSF_XML_NO_CONTENT, NULL, NULL),
1102 GSF_XML_IN_NODE_END
1104 static GsfXMLInDoc *doc = NULL;
1105 GuppiReadState *state;
1106 GogTheme *theme = gog_theme_registry_lookup ("Guppi");
1108 if (NULL == doc) {
1109 doc = gsf_xml_in_doc_new (dtd, NULL);
1110 gnm_xml_in_doc_dispose_on_exit (&doc);
1112 state = g_new0 (GuppiReadState, 1);
1113 state->graph = g_object_new (GOG_TYPE_GRAPH, NULL);
1114 gog_graph_set_theme (state->graph, theme);
1115 state->chart = gog_object_add_by_name (GOG_OBJECT (state->graph), "Chart", NULL);
1116 state->convs = convs;
1117 state->data = g_ptr_array_new ();
1118 state->max_data = 10;
1119 g_ptr_array_set_size (state->data, state->max_data);
1121 sheet_object_graph_set_gog (so, state->graph);
1122 gsf_xml_in_push_state (xin, doc, state,
1123 (GsfXMLInExtDtor) gnm_sogg_sax_parser_done, attrs);