Move plugin init code
[gnumeric.git] / src / sheet-object-graph.c
blob93007ee31b6422134e0285f157c30f7781b14aab
2 /*
3 * sheet-object-graph.c: Wrapper for GNOME Office graphs in gnumeric
5 * Copyright (C) 2003-2005 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) version 3.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
20 * USA
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
24 #include <gnumeric.h>
25 #include <sheet-object-graph.h>
27 #include <gnm-pane-impl.h>
28 #include <sheet-control-gui.h>
29 #include <gui-util.h>
30 #include <gui-file.h>
31 #include <gnm-graph-window.h>
32 #include <style-color.h>
33 #include <sheet-object-impl.h>
34 #include <wbc-gtk.h>
35 #include <commands.h>
36 #include <application.h>
37 #include <sheet.h>
38 #include <print-info.h>
39 #include <workbook.h>
40 #include <workbook-view.h>
41 #include <gutils.h>
42 #include <graph.h>
44 #include <goffice/goffice.h>
45 #include <goffice/canvas/goc-graph.h>
47 #include <gsf/gsf-impl-utils.h>
48 #include <gsf/gsf-utils.h>
49 #include <gsf/gsf-output-stdio.h>
50 #include <gsf/gsf-libxml.h>
51 #include <gdk/gdkkeysyms.h>
52 #include <math.h>
53 #include <string.h>
55 static void
56 so_graph_view_set_bounds (SheetObjectView *sov, double const *coords, gboolean visible)
58 GocItem *view = GOC_ITEM (GOC_GROUP (sov)->children->data);
59 double scale = goc_canvas_get_pixels_per_unit (view->canvas);
61 if (visible) {
62 goc_item_set (GOC_ITEM (sov),
63 "x", MIN (coords [0], coords[2]) / scale,
64 "y", MIN (coords [3], coords[1]) / scale,
65 NULL);
66 goc_item_set (view,
67 "width", (fabs (coords [2] - coords [0]) + 1.) / scale,
68 "height", (fabs (coords [3] - coords [1]) + 1.) / scale,
69 NULL);
71 goc_item_show (view);
72 } else
73 goc_item_hide (view);
76 static void
77 so_graph_goc_view_class_init (SheetObjectViewClass *sov_klass)
79 sov_klass->set_bounds = so_graph_view_set_bounds;
82 typedef SheetObjectView SOGraphGocView;
83 typedef SheetObjectViewClass SOGraphGocViewClass;
85 static GSF_CLASS (SOGraphGocView, so_graph_goc_view,
86 so_graph_goc_view_class_init, NULL,
87 GNM_SO_VIEW_TYPE)
89 /****************************************************************************/
90 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-graph-key"
92 #define SHEET_OBJECT_GRAPH_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_GRAPH_TYPE, SheetObjectGraphClass))
94 typedef struct {
95 SheetObject base;
96 GogGraph *graph;
97 GogRenderer *renderer;
98 gulong add_sig, remove_sig;
99 } SheetObjectGraph;
100 typedef SheetObjectClass SheetObjectGraphClass;
102 static GObjectClass *parent_klass;
104 static void
105 sog_data_set_sheet (G_GNUC_UNUSED SheetObjectGraph *sog,
106 GOData *data, Sheet *sheet)
108 gnm_go_data_set_sheet (data, sheet);
111 static void
112 sog_data_foreach_dep (SheetObject *so, GOData *data,
113 SheetObjectForeachDepFunc func, gpointer user)
115 gnm_go_data_foreach_dep (data, so, func, user);
118 static void
119 cb_graph_add_data (G_GNUC_UNUSED GogGraph *graph,
120 GOData *data, SheetObjectGraph *sog)
122 sog_data_set_sheet (sog, data, sog->base.sheet);
124 static void
125 cb_graph_remove_data (G_GNUC_UNUSED GogGraph *graph,
126 GOData *data, SheetObjectGraph *sog)
128 sog_data_set_sheet (sog, data, NULL);
131 static void
132 sog_datas_set_sheet (SheetObjectGraph *sog, Sheet *sheet)
134 GSList *ptr = gog_graph_get_data (sog->graph);
135 for (; ptr != NULL ; ptr = ptr->next)
136 sog_data_set_sheet (sog, ptr->data, sheet);
137 g_object_set (sog->graph, "document", ((sheet)? sheet->workbook: NULL), NULL);
140 static void
141 gnm_sog_finalize (GObject *obj)
143 SheetObjectGraph *sog = GNM_SO_GRAPH (obj);
145 if (sog->renderer != NULL) {
146 g_object_unref (sog->renderer);
147 sog->renderer = NULL;
149 if (sog->graph != NULL) {
150 g_object_unref (sog->graph);
151 sog->graph = NULL;
154 parent_klass->finalize (obj);
157 static void
158 cb_graph_size_changed (GocItem *item, GtkAllocation *allocation)
160 GogRenderer *rend;
161 GogGraph *graph;
162 SheetObject *so = sheet_object_view_get_so (GNM_SO_VIEW (item->parent));
163 GnmPrintInformation *pi = so->sheet->print_info;
164 double top, bottom, left, right, edge_to_below_header, edge_to_above_footer, w, h, x = 0., y = 0.;
165 w = print_info_get_paper_width (pi, GTK_UNIT_POINTS);
166 h = print_info_get_paper_height (pi, GTK_UNIT_POINTS);
167 print_info_get_margins (pi, &top, &bottom, &left, &right, &edge_to_below_header, &edge_to_above_footer);
168 w -= left + right;
169 h -= edge_to_above_footer + edge_to_below_header;
170 g_object_get (item, "renderer", &rend, NULL);
171 g_object_get (rend, "model", &graph, NULL);
172 gog_graph_set_size (graph, w, h);
173 if (w / allocation->width > h / allocation->height) {
174 h = allocation->width * h / w;
175 w = allocation->width;
176 y = (allocation->height - h) / 2.;
177 } else {
178 w = allocation->height * w / h;
179 h = allocation->height;
180 x = (allocation->width - w) / 2.;
182 goc_item_set (item, "x", x, "width", w, "y", y, "height", h, NULL);
183 g_object_unref (graph);
184 g_object_unref (rend);
187 static gboolean
188 cb_post_new_view (GocItem *item)
190 GtkAllocation alloc;
191 alloc.width = goc_canvas_get_width (item->canvas);
192 alloc.height = goc_canvas_get_height (item->canvas);
193 cb_graph_size_changed (item, &alloc);
194 return FALSE;
197 static SheetObjectView *
198 gnm_sog_new_view (SheetObject *so, SheetObjectViewContainer *container)
200 GnmPane *pane;
201 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
202 if (GNM_IS_PANE (container)) {
203 GocItem *view;
204 pane = GNM_PANE (container);
205 view = goc_item_new (pane->object_views,
206 so_graph_goc_view_get_type (),
207 NULL);
208 goc_item_new (GOC_GROUP (view),
209 GOC_TYPE_GRAPH,
210 "renderer", sog->renderer,
211 NULL);
212 return gnm_pane_object_register (so, view, TRUE);
213 } else {
214 GocCanvas *canvas = GOC_CANVAS (container);
215 GocItem *view = goc_item_new (goc_canvas_get_root (canvas),
216 so_graph_goc_view_get_type (),
217 NULL);
218 GocItem *item = goc_item_new (GOC_GROUP (view),
219 GOC_TYPE_GRAPH,
220 "renderer", sog->renderer,
221 NULL);
222 g_idle_add ((GSourceFunc) cb_post_new_view, item);
223 g_signal_connect_swapped (canvas, "size_allocate", G_CALLBACK (cb_graph_size_changed), item);
224 return (SheetObjectView *) view;
228 static GtkTargetList *
229 gnm_sog_get_target_list (G_GNUC_UNUSED SheetObject const *so)
231 GtkTargetList *tl = gtk_target_list_new (NULL, 0);
232 char *mime_str = go_image_format_to_mime ("svg");
233 GSList *mimes, *ptr;
235 mimes = go_strsplit_to_slist (mime_str, ',');
236 for (ptr = mimes; ptr != NULL; ptr = ptr->next) {
237 const char *mime = ptr->data;
239 if (mime != NULL && *mime != '\0')
240 gtk_target_list_add (tl, gdk_atom_intern (mime, FALSE),
241 0, 0);
243 g_free (mime_str);
244 g_slist_free_full (mimes, g_free);
245 /* No need to eliminate duplicates. */
246 gtk_target_list_add_image_targets (tl, 0, TRUE);
248 return tl;
251 static GtkTargetList *
252 gnm_sog_get_object_target_list (G_GNUC_UNUSED SheetObject const *so)
254 GtkTargetList *tl = gtk_target_list_new (NULL, 0);
255 gtk_target_list_add (tl,
256 gdk_atom_intern ("application/x-goffice-graph", FALSE), 0, 0);
257 return tl;
260 static void
261 gnm_sog_write_image (SheetObject const *so, char const *format, double resolution,
262 GsfOutput *output, GError **err)
264 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
265 gboolean res = FALSE;
266 double coords[4];
267 double w, h;
269 if (so->sheet) {
270 sheet_object_position_pts_get (GNM_SO (sog), coords);
271 w = fabs (coords[2] - coords[0]) + 1.;
272 h = fabs (coords[3] - coords[1]) + 1.;
273 } else {
274 w = GPOINTER_TO_UINT
275 (g_object_get_data (G_OBJECT (so), "pt-width-at-copy"));
276 h = GPOINTER_TO_UINT
277 (g_object_get_data (G_OBJECT (so), "pt-height-at-copy"));
280 g_return_if_fail (w > 0 && h > 0);
282 res = gog_graph_export_image (sog->graph, go_image_get_format_from_name (format),
283 output, resolution, resolution);
285 if (!res && err && *err == NULL)
286 *err = g_error_new (gsf_output_error_id (), 0,
287 _("Unknown failure while saving image"));
290 static void
291 gnm_sog_write_object (SheetObject const *so, char const *format,
292 GsfOutput *output, G_GNUC_UNUSED GError **err,
293 GnmConventions const *convs)
295 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
296 GsfXMLOut *xout;
297 GogObject *graph;
299 g_return_if_fail (strcmp (format, "application/x-goffice-graph") == 0);
301 graph = gog_object_dup (GOG_OBJECT (sog->graph),
302 NULL, gog_dataset_dup_to_simple);
303 xout = gsf_xml_out_new (output);
304 gog_object_write_xml_sax (GOG_OBJECT (graph), xout, (gpointer)convs);
305 g_object_unref (xout);
306 g_object_unref (graph);
309 static void
310 sog_cb_save_as (SheetObject *so, SheetControl *sc)
312 WBCGtk *wbcg;
313 char *uri;
314 GError *err = NULL;
315 GsfOutput *output;
316 GSList *l;
317 GOImageFormat selected_format;
318 GOImageFormatInfo const *format_info;
319 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
320 double resolution;
322 g_return_if_fail (sog != NULL);
324 l = gog_graph_get_supported_image_formats ();
325 g_return_if_fail (l != NULL);
326 selected_format = GPOINTER_TO_UINT (l->data);
328 #warning "This violates model gui barrier"
329 wbcg = scg_wbcg (GNM_SCG (sc));
330 uri = go_gui_get_image_save_info (wbcg_toplevel (wbcg), l, &selected_format, &resolution);
331 if (!uri)
332 goto out;
333 output = go_file_create (uri, &err);
334 if (!output)
335 goto out;
336 format_info = go_image_get_format_info (selected_format);
337 sheet_object_write_image (so, format_info->name, resolution, output, &err);
338 g_object_unref (output);
340 if (err != NULL)
341 go_cmd_context_error (GO_CMD_CONTEXT (wbcg), err);
343 out:
344 g_free (uri);
345 g_slist_free (l);
348 static void
349 sog_cb_open_in_new_window (SheetObject *so, SheetControl *sc)
351 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
352 SheetControlGUI *scg = GNM_SCG (sc);
353 WBCGtk *wbcg = scg_wbcg (scg);
354 GtkWidget *window;
355 double coords[4];
357 g_return_if_fail (sog != NULL);
359 scg_object_anchor_to_coords (scg, sheet_object_get_anchor (so), coords);
360 window = gnm_graph_window_new (sog->graph,
361 floor (fabs (coords[2] - coords[0]) + 0.5),
362 floor (fabs (coords[3] - coords[1]) + 0.5));
363 gtk_window_set_screen (GTK_WINDOW (window),
364 gtk_window_get_screen (wbcg_toplevel (wbcg)));
365 gtk_window_present (GTK_WINDOW (window));
366 g_signal_connect (window, "delete-event",
367 G_CALLBACK (gtk_widget_destroy),
368 NULL);
371 static void
372 sog_cb_copy_to_new_sheet (SheetObject *so, SheetControl *sc)
374 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
375 SheetControlGUI *scg = GNM_SCG (sc);
376 WorkbookControl *wbc = scg_wbc (scg);
377 Sheet *sheet = wb_control_cur_sheet (wbc);
378 GogGraph *graph = GOG_GRAPH (gog_object_dup (GOG_OBJECT (sog->graph), NULL, NULL));
379 WorkbookSheetState *old_state = workbook_sheet_state_new (wb_control_get_workbook (wbc));
380 Sheet *new_sheet = workbook_sheet_add_with_type (
381 wb_control_get_workbook (wbc),
382 GNM_SHEET_OBJECT, -1,
383 gnm_sheet_get_max_cols (sheet),
384 gnm_sheet_get_max_rows (sheet));
385 SheetObject *new_sog = sheet_object_graph_new (graph);
386 print_info_set_paper_orientation (new_sheet->print_info, GTK_PAGE_ORIENTATION_LANDSCAPE);
387 sheet_object_set_sheet (new_sog, new_sheet);
388 wb_view_sheet_focus (wb_control_view (wbc), new_sheet);
389 cmd_reorganize_sheets (wbc, old_state, sheet);
390 g_object_unref (graph);
391 g_object_unref (new_sog);
394 static void
395 gnm_sog_populate_menu (SheetObject *so, GPtrArray *actions)
397 static SheetObjectAction const sog_actions[] = {
398 { "document-save-as", N_("_Save As Image"), NULL, 0, sog_cb_save_as, NULL },
399 { NULL, N_("Open in _New Window"), NULL, 0, sog_cb_open_in_new_window, NULL },
400 { NULL, N_("Copy to New Graph S_heet"), NULL, 0, sog_cb_copy_to_new_sheet, NULL }
403 unsigned int i;
405 GNM_SO_CLASS (parent_klass)->populate_menu (so, actions);
407 for (i = 0; i < G_N_ELEMENTS (sog_actions); i++)
408 g_ptr_array_insert (actions, 1 + i, (gpointer) (sog_actions + i));
411 static void
412 gnm_sog_write_xml_sax (SheetObject const *so, GsfXMLOut *output,
413 GnmConventions const *convs)
415 SheetObjectGraph const *sog = GNM_SO_GRAPH (so);
416 gog_object_write_xml_sax (GOG_OBJECT (sog->graph), output,
417 (gpointer)convs);
420 static void
421 sog_xml_finish (GogObject *graph, SheetObject *so)
423 sheet_object_graph_set_gog (so, GOG_GRAPH (graph));
424 g_object_unref (graph);
427 static void gnm_sogg_prep_sax_parser (SheetObject *so, GsfXMLIn *xin,
428 xmlChar const **attrs,
429 GnmConventions const *convs);
430 static void
431 gnm_sog_prep_sax_parser (SheetObject *so, GsfXMLIn *xin, xmlChar const **attrs,
432 GnmConventions const *convs)
434 if (strcmp (xin->node->name, "GnmGraph"))
435 gog_object_sax_push_parser (xin, attrs,
436 (GogObjectSaxHandler) sog_xml_finish,
437 (gpointer)convs, so);
438 else
439 gnm_sogg_prep_sax_parser (so, xin, attrs, convs);
442 static void
443 gnm_sog_copy (SheetObject *dst, SheetObject const *src)
445 SheetObjectGraph const *sog = GNM_SO_GRAPH (src);
446 GogGraph *graph = gog_graph_dup (sog->graph);
447 sheet_object_graph_set_gog (dst, graph);
448 g_object_unref (graph);
451 static void
452 gnm_sog_draw_cairo (SheetObject const *so, cairo_t *cr,
453 double width, double height)
455 gog_graph_render_to_cairo (GNM_SO_GRAPH (so)->graph,
456 cr, width, height);
459 typedef struct {
460 SheetObject *so;
461 WorkbookControl *wbc;
462 } gnm_sog_user_config_t;
464 static void
465 gnm_sog_user_config_free_data (gpointer data,
466 GClosure *closure)
468 g_free (data);
469 closure->data = NULL;
472 static void
473 cb_update_graph (GogGraph *graph, gnm_sog_user_config_t *data)
475 cmd_so_graph_config (data->wbc, data->so, G_OBJECT (graph),
476 G_OBJECT (GNM_SO_GRAPH (data->so)->graph));
479 static void
480 gnm_sog_user_config (SheetObject *so, SheetControl *sc)
482 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
483 WBCGtk *wbcg;
484 gnm_sog_user_config_t *data;
485 GClosure *closure;
487 g_return_if_fail (sog != NULL);
488 g_return_if_fail (sc != NULL);
490 wbcg = scg_wbcg (GNM_SCG (sc));
492 data = g_new0 (gnm_sog_user_config_t, 1);
493 data->so = so;
494 data->wbc = GNM_WBC (wbcg);
496 closure = g_cclosure_new (G_CALLBACK (cb_update_graph), data,
497 (GClosureNotify) gnm_sog_user_config_free_data);
499 sheet_object_graph_guru (wbcg, sog->graph, closure);
500 g_closure_sink (closure);
503 static void
504 gnm_sog_foreach_dep (SheetObject *so,
505 SheetObjectForeachDepFunc func,
506 gpointer user)
508 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
509 GSList *ptr = gog_graph_get_data (sog->graph);
510 for (; ptr != NULL ; ptr = ptr->next)
511 sog_data_foreach_dep (so, ptr->data, func, user);
514 static gboolean
515 gnm_sog_set_sheet (SheetObject *so, Sheet *sheet)
517 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
518 if (sog->graph != NULL)
519 sog_datas_set_sheet (sog, sheet);
520 return FALSE;
523 static gboolean
524 gnm_sog_remove_from_sheet (SheetObject *so)
526 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
527 if (sog->graph != NULL)
528 sog_datas_set_sheet (sog, NULL);
529 return FALSE;
532 static void
533 gnm_sog_default_size (G_GNUC_UNUSED SheetObject const *so, double *w, double *h)
535 *w = GO_CM_TO_PT ((double)12);
536 *h = GO_CM_TO_PT ((double)8);
539 static void
540 gnm_sog_bounds_changed (SheetObject *so)
542 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
544 /* If it has not been realized there is no renderer yet */
545 if (sog->renderer != NULL) {
546 double coords [4];
547 if (so->sheet->sheet_type == GNM_SHEET_DATA) {
548 sheet_object_position_pts_get (so, coords);
549 gog_graph_set_size (sog->graph, fabs (coords[2] - coords[0]),
550 fabs (coords[3] - coords[1]));
551 } else {
552 /*FIXME: get dimensions from print settings */
553 gog_graph_set_size (sog->graph, 400., 300.);
558 static void
559 gnm_sog_class_init (GObjectClass *klass)
561 SheetObjectClass *so_class = GNM_SO_CLASS (klass);
563 parent_klass = g_type_class_peek_parent (klass);
565 /* Object class method overrides */
566 klass->finalize = gnm_sog_finalize;
568 /* SheetObject class method overrides */
569 so_class->new_view = gnm_sog_new_view;
570 so_class->bounds_changed = gnm_sog_bounds_changed;
571 so_class->populate_menu = gnm_sog_populate_menu;
572 so_class->write_xml_sax = gnm_sog_write_xml_sax;
573 so_class->prep_sax_parser = gnm_sog_prep_sax_parser;
574 so_class->copy = gnm_sog_copy;
575 so_class->user_config = gnm_sog_user_config;
576 so_class->assign_to_sheet = gnm_sog_set_sheet;
577 so_class->remove_from_sheet = gnm_sog_remove_from_sheet;
578 so_class->default_size = gnm_sog_default_size;
579 so_class->draw_cairo = gnm_sog_draw_cairo;
580 so_class->foreach_dep = gnm_sog_foreach_dep;
582 so_class->rubber_band_directly = FALSE;
585 static void
586 gnm_sog_init (GObject *obj)
588 SheetObject *so = GNM_SO (obj);
589 so->anchor.base.direction = GOD_ANCHOR_DIR_DOWN_RIGHT;
592 static void
593 sog_imageable_init (SheetObjectImageableIface *soi_iface)
595 soi_iface->get_target_list = gnm_sog_get_target_list;
596 soi_iface->write_image = gnm_sog_write_image;
599 static void
600 sog_exportable_init (SheetObjectExportableIface *soe_iface)
602 soe_iface->get_target_list = gnm_sog_get_object_target_list;
603 soe_iface->write_object = gnm_sog_write_object;
606 GSF_CLASS_FULL (SheetObjectGraph, sheet_object_graph,
607 NULL, NULL, gnm_sog_class_init,NULL,
608 gnm_sog_init, GNM_SO_TYPE, 0,
609 GSF_INTERFACE (sog_imageable_init, GNM_SO_IMAGEABLE_TYPE) \
610 GSF_INTERFACE (sog_exportable_init, GNM_SO_EXPORTABLE_TYPE))
613 * sheet_object_graph_new:
614 * @graph: (allow-none): #GogGraph
616 * Adds a reference to @graph and creates a gnumeric sheet object wrapper
617 * Returns: (transfer full): the newly allocated #SheetObject.
619 SheetObject *
620 sheet_object_graph_new (GogGraph *graph)
622 SheetObject *sog = g_object_new (GNM_SO_GRAPH_TYPE, NULL);
623 GnmGraphDataClosure *data = graph
624 ? (GnmGraphDataClosure *) g_object_get_data (G_OBJECT (graph), "data-closure")
625 : NULL;
626 sheet_object_graph_set_gog (sog, graph);
627 if (data != NULL)
628 sog->anchor.mode = data->anchor_mode;
630 return sog;
634 * sheet_object_graph_get_gog:
635 * @sog: #SheetObject
637 * Returns: (transfer none): the embedded #GogGraph or %NULL on error.
640 GogGraph *
641 sheet_object_graph_get_gog (SheetObject *sog)
643 g_return_val_if_fail (GNM_IS_SO_GRAPH (sog), NULL);
645 return ((SheetObjectGraph *)sog)->graph;
649 * sheet_object_graph_set_gog:
650 * @sog: #SheetObjectGraph
651 * @graph: (allow-none): #GogGraph
653 * If @graph is non NULL add a reference to it, otherwise create a new graph.
654 * Assign the graph to its SheetObjectGraph wrapper and initialize the
655 * handlers, disconnecting any existing connection for the preceding graph.
657 void
658 sheet_object_graph_set_gog (SheetObject *so, GogGraph *graph)
660 SheetObjectGraph *sog = GNM_SO_GRAPH (so);
662 g_return_if_fail (GNM_IS_SO_GRAPH (so));
664 if (graph != NULL) {
665 if (sog->graph == graph)
666 return;
668 g_object_ref (graph);
669 } else
670 graph = g_object_new (GOG_TYPE_GRAPH, NULL);
672 if (sog->graph != NULL) {
673 g_signal_handler_disconnect (sog->graph, sog->add_sig);
674 g_signal_handler_disconnect (sog->graph, sog->remove_sig);
675 if (so->sheet != NULL)
676 sog_datas_set_sheet (sog, NULL);
677 g_object_unref (sog->graph);
680 sog->graph = graph;
681 if (so->sheet != NULL)
682 sog_datas_set_sheet (sog, so->sheet);
683 sog->add_sig = g_signal_connect_object (G_OBJECT (graph),
684 "add_data",
685 G_CALLBACK (cb_graph_add_data), G_OBJECT (sog), 0);
686 sog->remove_sig = g_signal_connect_object (G_OBJECT (graph),
687 "remove_data",
688 G_CALLBACK (cb_graph_remove_data), G_OBJECT (sog), 0);
690 if (sog->renderer != NULL)
691 g_object_set (sog->renderer, "model", graph, NULL);
692 else
693 sog->renderer = gog_renderer_new (sog->graph);
696 static void
697 cb_graph_guru_done (WBCGtk *wbcg)
699 wbcg_edit_finish (wbcg, WBC_EDIT_REJECT, NULL);
702 static void
703 cb_graph_data_closure_done (GnmGraphDataClosure *data)
705 if (data->obj)
706 g_object_set_data (data->obj,"data-closure", NULL);
707 g_free (data);
710 static void
711 cb_selection_mode_changed (GtkComboBox *box, GnmGraphDataClosure *data)
713 GogObject *graph = (GogObject *) g_object_get_data (data->obj, "graph");
714 data->colrowmode = gtk_combo_box_get_active (box);
715 if (graph) {
716 GogObject *gobj = gog_object_get_child_by_name (graph, "Chart");
717 gobj = gog_object_get_child_by_name (gobj, "Plot");
718 if (!gobj)
719 return;
720 gog_plot_clear_series (GOG_PLOT (gobj));
721 gog_data_allocator_allocate (data->dalloc, GOG_PLOT (gobj));
725 static void
726 cb_shared_mode_changed (GtkToggleButton *btn, GnmGraphDataClosure *data)
728 GogObject *graph = (GogObject *) g_object_get_data (data->obj, "graph");
729 data->share_x = gtk_toggle_button_get_active (btn);
730 if (graph) {
731 GogObject *gobj = gog_object_get_child_by_name (graph, "Chart");
732 gobj = gog_object_get_child_by_name (gobj, "Plot");
733 if (!gobj)
734 return;
735 gog_plot_clear_series (GOG_PLOT (gobj));
736 gog_data_allocator_allocate (data->dalloc, GOG_PLOT (gobj));
740 static void
741 cb_sheet_target_changed (GtkToggleButton *btn, GnmGraphDataClosure *data)
743 data->new_sheet = gtk_toggle_button_get_active (btn);
746 void
747 sheet_object_graph_guru (WBCGtk *wbcg, GogGraph *graph,
748 GClosure *closure)
750 GtkWidget *dialog = gog_guru (graph, GOG_DATA_ALLOCATOR (wbcg),
751 GO_CMD_CONTEXT (wbcg), closure);
752 if (!graph) {
753 GnmGraphDataClosure *data = (GnmGraphDataClosure *) g_new0 (GnmGraphDataClosure, 1);
754 GtkWidget *custom = gtk_grid_new (), *w;
755 GObject *object;
757 data->dalloc = GOG_DATA_ALLOCATOR (wbcg);
758 g_object_set (custom,
759 "row-spacing", 6,
760 "column-spacing", 12,
761 "margin-top", 6,
762 NULL);
763 w = gtk_label_new (_("Series as:"));
764 g_object_set (w, "xalign", 0., NULL);
765 gtk_grid_attach (GTK_GRID (custom), w, 0, 0, 1, 1);
766 w = gtk_combo_box_text_new ();
767 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), _("Auto"));
768 /*Translators: Series as "Columns"*/
769 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), C_("graph", "Columns"));
770 /*Translators: Series as "Rows"*/
771 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (w), C_("graph", "Rows"));
772 gtk_combo_box_set_active (GTK_COMBO_BOX (w), 0);
773 g_signal_connect (G_OBJECT (w), "changed", G_CALLBACK (cb_selection_mode_changed), data);
774 gtk_grid_attach (GTK_GRID (custom), w, 1, 0, 1, 1);
775 w = gtk_check_button_new_with_label (_("Use first series as shared abscissa"));
776 g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (cb_shared_mode_changed), data);
777 gtk_grid_attach (GTK_GRID (custom), w, 0, 1, 2, 1);
778 w = gtk_check_button_new_with_label (_("New graph sheet"));
779 g_signal_connect (G_OBJECT (w), "toggled", G_CALLBACK (cb_sheet_target_changed), data);
780 gtk_grid_attach (GTK_GRID (custom), w, 0, 2, 2, 1);
781 data->obj = G_OBJECT (custom);
782 data->anchor_mode = GNM_SO_ANCHOR_ONE_CELL; /* don't resize graphs with cells, see # */
783 gog_guru_add_custom_widget (dialog, custom);
784 object = (GObject*) g_object_get_data (data->obj, "graph");
785 if (object)
786 g_object_set_data (object, "data-closure", data);
787 g_object_set_data_full (G_OBJECT (custom), "data-closure", data, (GDestroyNotify) cb_graph_data_closure_done);
789 gnm_init_help_button (
790 gog_guru_get_help_button (dialog),
791 "chapter-graphs");
792 gnm_dialog_setup_destroy_handlers (GTK_DIALOG (dialog),
793 wbcg, GNM_DIALOG_DESTROY_SHEET_REMOVED);
794 gnm_keyed_dialog (wbcg, GTK_WINDOW (dialog), "graph-guru");
795 g_object_set_data_full (G_OBJECT (dialog),
796 "guru", wbcg, (GDestroyNotify) cb_graph_guru_done);
797 wbc_gtk_attach_guru (wbcg, dialog);
798 gtk_widget_show (dialog);
802 * sheet_object_graph_ensure_size:
803 * @sog: #SheetObject
805 * Updates the size of the graph item in the canvas for graph sheets objects.
807 void
808 sheet_object_graph_ensure_size (SheetObject *sog)
810 GList *ptr = sog->realized_list;
811 while (ptr) {
812 cb_post_new_view (GOC_ITEM (GOC_GROUP (ptr->data)->children->data));
813 ptr = ptr->next;
817 /*****************************************************************************/
818 /* Support for Guppi graphs */
820 typedef struct {
821 GnmConventions const *convs;
822 GogGraph *graph;
823 GogObject *chart;
824 GogPlot *plot;
825 GogObject *cur;
826 GogStyle *style;
827 GPtrArray *data;
828 unsigned cur_index, max_data;
829 } GuppiReadState;
831 static void
832 vector_start (GsfXMLIn *xin, xmlChar const **attrs)
834 GuppiReadState *state = (GuppiReadState *) xin->user_state;
835 int i;
836 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
837 if (0 == strcmp (attrs[i], "ID"))
838 state->cur_index = strtoul (attrs[i+1], NULL, 10);
839 if (state->cur_index < 256 && state->cur_index >= state->max_data) {
840 state->max_data += 10;
841 g_ptr_array_set_size (state->data, state->max_data);
845 static void
846 vector_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
848 GuppiReadState *state = (GuppiReadState *) xin->user_state;
849 GOData *data;
850 if (state->cur_index >= state->max_data)
851 return;
852 data = g_object_new (GNM_GO_DATA_VECTOR_TYPE, NULL);
853 go_data_unserialize (data, xin->content->str, (void*) state->convs);
854 g_ptr_array_index (state->data, state->cur_index) = data;
857 static void
858 plot_type_start (GsfXMLIn *xin, xmlChar const **attrs)
860 GuppiReadState *state = (GuppiReadState *) xin->user_state;
861 int i;
862 char const *name = NULL;
863 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
864 if (0 == strcmp (attrs[i], "name"))
865 name = attrs[i+1];
866 if (name) {
867 if (0 == strcmp (name, "Scatter")) {
868 state->plot = gog_plot_new_by_name ("GogXYPlot");
869 g_object_set (G_OBJECT (state->plot),
870 "default-style-has-markers", FALSE,
871 "default-style-has-lines", FALSE,
872 NULL);
873 gog_object_add_by_name (state->chart, "Backplane", NULL);
875 else if (0 == strcmp (name, "Pie"))
876 state->plot = gog_plot_new_by_name ("GogPiePlot");
877 else if (0 == strcmp (name, "Bar")) {
878 state->plot = gog_plot_new_by_name ("GogBarColPlot");
879 gog_object_add_by_name (state->chart, "Backplane", NULL);
880 } else if (0 == strcmp (name, "Line")) {
881 state->plot = gog_plot_new_by_name ("GogLinePlot");
882 g_object_set (G_OBJECT (state->plot),
883 "default-style-has-markers", FALSE,
884 NULL);
885 gog_object_add_by_name (state->chart, "Backplane", NULL);
887 if (state->plot)
888 gog_object_add_by_name (GOG_OBJECT (state->chart), "Plot", GOG_OBJECT (state->plot));
892 static void
893 legend_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
895 GuppiReadState *state = (GuppiReadState *) xin->user_state;
896 state->cur = gog_object_add_by_name (state->chart, "Legend", NULL);
899 static void
900 position_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
902 GuppiReadState *state = (GuppiReadState *)xin->user_state;
903 if (!xin->content->str)
904 return;
905 if (0 == strcmp (xin->content->str, "east"))
906 g_object_set (G_OBJECT (state->cur), "compass", "right", NULL);
907 if (0 == strcmp (xin->content->str, "west"))
908 g_object_set (G_OBJECT (state->cur), "compass", "left", NULL);
909 if (0 == strcmp (xin->content->str, "north"))
910 g_object_set (G_OBJECT (state->cur), "compass", "top", NULL);
911 if (0 == strcmp (xin->content->str, "south"))
912 g_object_set (G_OBJECT (state->cur), "compass", "bottom", NULL);
915 static void
916 series_start (GsfXMLIn *xin, xmlChar const **attrs)
918 GuppiReadState *state = (GuppiReadState *) xin->user_state;
919 int i;
920 char *name = NULL;
921 GError *err = NULL;
922 GOData *data;
924 state->cur = GOG_OBJECT (gog_plot_new_series (state->plot));
925 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
926 if (0 == strcmp (attrs[i], "name"))
927 name = g_strdup_printf ("\"%s\"", attrs[i+1]);
928 if (name) {
929 data = g_object_new (GNM_GO_DATA_SCALAR_TYPE, NULL);
930 go_data_unserialize (data, name, (void*) state->convs);
931 gog_dataset_set_dim (GOG_DATASET (state->cur), -1,
932 data, &err);
933 g_free (name);
935 if (err)
936 g_error_free (err);
939 static void
940 dim_start (GsfXMLIn *xin, xmlChar const **attrs)
942 GuppiReadState *state = (GuppiReadState *) xin->user_state;
943 unsigned i, id = 0;
944 char const *name = "?";
945 GogMSDimType type = GOG_MS_DIM_LABELS;
946 GogPlotDesc const *desc = gog_plot_description (state->plot);
947 GError *err = NULL;
948 for (i = 0; attrs != NULL && attrs[i] && attrs[i+1] ; i += 2)
949 if (0 == strcmp (attrs[i], "dim_name"))
950 name = attrs[i+1];
951 else if (0 == strcmp (attrs[i], "ID"))
952 id = strtoul (attrs[i+1], NULL, 10);
953 if (!desc ||
954 id >= state->data->len || g_ptr_array_index (state->data, id) == NULL)
955 return;
956 if (0 == strcmp (name, "values"))
957 type = GOG_MS_DIM_VALUES;
958 else if (0 == strcmp (name, "categories"))
959 type = GOG_MS_DIM_CATEGORIES;
960 else if (0 == strcmp (name, "bubbles"))
961 type = GOG_MS_DIM_BUBBLES;
962 for (i = 0; i < desc->series.num_dim; i++)
963 if (desc->series.dim[i].ms_type == type) {
964 GOData *data = g_object_ref (g_ptr_array_index (state->data, id));
965 gog_dataset_set_dim (GOG_DATASET (state->cur), i,data,
966 &err);
967 break;
969 if (err)
970 g_error_free (err);
973 static void
974 marker_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
976 GuppiReadState *state = (GuppiReadState *)xin->user_state;
977 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
978 g_object_set (G_OBJECT (state->plot), "default-style-has-markers", TRUE, NULL);
981 static void
982 linear_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
984 GuppiReadState *state = (GuppiReadState *)xin->user_state;
985 g_object_set (G_OBJECT (state->plot),
986 "default-style-has-lines", TRUE,
987 NULL);
990 static void
991 cubic_start (GsfXMLIn *xin, G_GNUC_UNUSED xmlChar const **attrs)
993 GuppiReadState *state = (GuppiReadState *)xin->user_state;
994 g_object_set (G_OBJECT (state->plot),
995 "default-style-has-lines", TRUE,
996 "use-splines", TRUE,
997 NULL);
1000 static void
1001 horiz_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1003 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1004 if (xin->content->str)
1005 g_object_set (G_OBJECT (state->plot), "horizontal", 0 == strcmp (xin->content->str, "true"), NULL);
1008 static void
1009 stacked_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1011 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1012 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
1013 g_object_set (G_OBJECT (state->plot), "type", "stacked", NULL);
1016 static void
1017 percent_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1019 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1020 if (xin->content->str && 0 == strcmp (xin->content->str, "true"))
1021 g_object_set (G_OBJECT (state->plot), "type", "as_percentage", NULL);
1024 static void
1025 separation_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1027 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1028 if (xin->content->str) {
1029 double separation = g_ascii_strtod (xin->content->str, NULL);
1030 g_object_set (G_OBJECT (state->plot),
1031 "default-separation", separation / 100.,
1032 NULL);
1036 static void
1037 bubble_end (GsfXMLIn *xin, G_GNUC_UNUSED GsfXMLBlob *unknown)
1039 GuppiReadState *state = (GuppiReadState *)xin->user_state;
1040 if (xin->content->str && 0 == strcmp (xin->content->str, "true")) {
1041 g_object_unref (state->plot);
1042 state->plot = gog_plot_new_by_name ("GogBubblePlot");
1043 gog_object_add_by_name (state->chart, "Backplane", NULL);
1047 static void
1048 gnm_sogg_sax_parser_done (G_GNUC_UNUSED GsfXMLIn *xin, GuppiReadState *state)
1050 unsigned i;
1051 GObject *obj;
1052 g_object_unref (state->graph);
1053 for (i = 0; i < state->max_data; i++) {
1054 obj = (GObject *) g_ptr_array_index (state->data, i);
1055 if (obj)
1056 g_object_unref (obj);
1058 g_ptr_array_free (state->data, TRUE);
1059 g_free (state);
1062 static void
1063 gnm_sogg_prep_sax_parser (SheetObject *so, GsfXMLIn *xin, xmlChar const **attrs,
1064 GnmConventions const *convs)
1066 static GsfXMLInNode const dtd[] = {
1067 GSF_XML_IN_NODE (GRAPH, GRAPH, -1, "GmrGraph", GSF_XML_NO_CONTENT, NULL, NULL),
1068 GSF_XML_IN_NODE (GRAPH, GUPPI_VECTORS, -1, "gmr:Vectors", GSF_XML_NO_CONTENT, NULL, NULL),
1069 GSF_XML_IN_NODE (GUPPI_VECTORS, GUPPI_VECTOR, -1, "gmr:Vector", GSF_XML_CONTENT, vector_start, vector_end),
1070 GSF_XML_IN_NODE (GRAPH, GUPPI_GRAPH, -1, "graph:Graph", GSF_XML_NO_CONTENT, NULL, NULL),
1071 GSF_XML_IN_NODE (GUPPI_GRAPH, GUPPI_LEGEND, -1, "graph:Legend", GSF_XML_NO_CONTENT, legend_start, NULL),
1072 GSF_XML_IN_NODE (GUPPI_LEGEND, GUPPI_LEGEND_POS, -1, "graph:Position", GSF_XML_CONTENT, NULL, position_end),
1073 GSF_XML_IN_NODE (GUPPI_GRAPH, GUPPI_PLOTS, -1, "graph:Plots", GSF_XML_NO_CONTENT, NULL, NULL),
1074 GSF_XML_IN_NODE (GUPPI_PLOTS, GUPPI_PLOT, -1, "graph:Plot", GSF_XML_NO_CONTENT, NULL, NULL),
1075 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_PLOT_TYPE, -1, "Type", GSF_XML_NO_CONTENT, plot_type_start, NULL),
1076 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_MARKER, -1, "with_marker", GSF_XML_CONTENT, NULL, marker_end),
1077 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_MARKERS, -1, "with_markers", GSF_XML_CONTENT, NULL, marker_end),
1078 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_LINES, -1, "with_line", GSF_XML_NO_CONTENT, NULL, NULL),
1079 GSF_XML_IN_NODE (GUPPI_LINES, GUPPI_PLOT_LINEAR, -1, "Linear", GSF_XML_NO_CONTENT, linear_start, NULL),
1080 GSF_XML_IN_NODE (GUPPI_LINES, GUPPI_PLOT_CUBIC, -1, "Cubic", GSF_XML_NO_CONTENT, cubic_start, NULL),
1081 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_HORIZONTAL, -1, "horizontal", GSF_XML_CONTENT, NULL, horiz_end),
1082 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_STACKED, -1, "stacked", GSF_XML_CONTENT, NULL, stacked_end),
1083 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_AS_PERCENT, -1, "as_percentage", GSF_XML_CONTENT, NULL, percent_end),
1084 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_SEPARATION, -1, "separation_percent_of_radius", GSF_XML_CONTENT, NULL, separation_end),
1085 GSF_XML_IN_NODE (GUPPI_PLOT_TYPE, GUPPI_BUBBLE, -1, "auto_allocate_bubble_size", GSF_XML_CONTENT, NULL, bubble_end),
1086 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_PLOT_GRAPH_TYPE, -1, "graph:Type", GSF_XML_NO_CONTENT, plot_type_start, NULL),
1087 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_MARKER, -1, "with_marker", GSF_XML_2ND, NULL, NULL),
1088 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_MARKERS, -1, "with_markers", GSF_XML_2ND, NULL, NULL),
1089 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_LINES, -1, "with_line", GSF_XML_2ND, NULL, NULL),
1090 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_HORIZONTAL, -1, "horizontal", GSF_XML_2ND, NULL, NULL),
1091 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_STACKED, -1, "stacked", GSF_XML_2ND, NULL, NULL),
1092 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_AS_PERCENT, -1, "as_percentage", GSF_XML_2ND, NULL, NULL),
1093 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_SEPARATION, -1, "separation_percent_of_radius", GSF_XML_2ND, NULL, NULL),
1094 GSF_XML_IN_NODE (GUPPI_PLOT_GRAPH_TYPE, GUPPI_BUBBLE, -1, "auto_allocate_bubble_size", GSF_XML_2ND, NULL, NULL),
1095 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_DATA, -1, "graph:Data", GSF_XML_NO_CONTENT, NULL, NULL),
1096 GSF_XML_IN_NODE (GUPPI_DATA, GUPPI_SERIES, -1, "graph:Series", GSF_XML_NO_CONTENT, series_start, NULL),
1097 GSF_XML_IN_NODE (GUPPI_SERIES, GUPPI_SERIES_DIM, -1, "graph:Dimension", GSF_XML_NO_CONTENT, dim_start, NULL),
1098 GSF_XML_IN_NODE (GUPPI_PLOT, GUPPI_DATA_LAYOUT, -1, "graph:DataLayout", GSF_XML_NO_CONTENT, NULL, NULL),
1099 GSF_XML_IN_NODE (GUPPI_DATA_LAYOUT, GUPPI_DIMENSION, -1, "graph:Dimension", GSF_XML_NO_CONTENT, NULL, NULL),
1100 GSF_XML_IN_NODE_END
1102 static GsfXMLInDoc *doc = NULL;
1103 GuppiReadState *state;
1104 GogTheme *theme = gog_theme_registry_lookup ("Guppi");
1106 if (NULL == doc) {
1107 doc = gsf_xml_in_doc_new (dtd, NULL);
1108 gnm_xml_in_doc_dispose_on_exit (&doc);
1110 state = g_new0 (GuppiReadState, 1);
1111 state->graph = g_object_new (GOG_TYPE_GRAPH, NULL);
1112 gog_graph_set_theme (state->graph, theme);
1113 state->chart = gog_object_add_by_name (GOG_OBJECT (state->graph), "Chart", NULL);
1114 state->convs = convs;
1115 state->data = g_ptr_array_new ();
1116 state->max_data = 10;
1117 g_ptr_array_set_size (state->data, state->max_data);
1119 sheet_object_graph_set_gog (so, state->graph);
1120 gsf_xml_in_push_state (xin, doc, state,
1121 (GsfXMLInExtDtor) gnm_sogg_sax_parser_done, attrs);