1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
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
23 #include <gnumeric-config.h>
24 #include <glib/gi18n-lib.h>
26 #include "sheet-object-graph.h"
28 #include "gnm-pane-impl.h"
29 #include "sheet-control-gui.h"
32 #include "gnm-graph-window.h"
33 #include "style-color.h"
34 #include "sheet-object-impl.h"
37 #include "application.h"
39 #include "print-info.h"
41 #include "workbook-view.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>
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
);
64 goc_item_set (GOC_ITEM (sov
),
65 "x", MIN (coords
[0], coords
[2]) / scale
,
66 "y", MIN (coords
[3], coords
[1]) / scale
,
69 "width", (fabs (coords
[2] - coords
[0]) + 1.) / scale
,
70 "height", (fabs (coords
[3] - coords
[1]) + 1.) / scale
,
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
,
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))
99 GogRenderer
*renderer
;
100 gulong add_sig
, remove_sig
;
102 typedef SheetObjectClass SheetObjectGraphClass
;
104 static GObjectClass
*parent_klass
;
107 sog_data_set_sheet (G_GNUC_UNUSED SheetObjectGraph
*sog
,
108 GOData
*data
, Sheet
*sheet
)
110 gnm_go_data_set_sheet (data
, sheet
);
114 sog_data_foreach_dep (SheetObject
*so
, GOData
*data
,
115 SheetObjectForeachDepFunc func
, gpointer user
)
117 gnm_go_data_foreach_dep (data
, so
, func
, user
);
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
);
127 cb_graph_remove_data (G_GNUC_UNUSED GogGraph
*graph
,
128 GOData
*data
, SheetObjectGraph
*sog
)
130 sog_data_set_sheet (sog
, data
, NULL
);
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
);
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
);
156 parent_klass
->finalize (obj
);
160 cb_graph_size_changed (GocItem
*item
, GtkAllocation
*allocation
)
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
);
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.;
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
);
190 cb_post_new_view (GocItem
*item
)
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
);
199 static SheetObjectView
*
200 gnm_sog_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
203 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
204 if (GNM_IS_PANE (container
)) {
206 pane
= GNM_PANE (container
);
207 view
= goc_item_new (pane
->object_views
,
208 so_graph_goc_view_get_type (),
210 goc_item_new (GOC_GROUP (view
),
212 "renderer", sog
->renderer
,
214 return gnm_pane_object_register (so
, view
, TRUE
);
216 GocCanvas
*canvas
= GOC_CANVAS (container
);
217 GocItem
*view
= goc_item_new (goc_canvas_get_root (canvas
),
218 so_graph_goc_view_get_type (),
220 GocItem
*item
= goc_item_new (GOC_GROUP (view
),
222 "renderer", sog
->renderer
,
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");
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
),
246 g_slist_free_full (mimes
, g_free
);
247 /* No need to eliminate duplicates. */
248 gtk_target_list_add_image_targets (tl
, 0, TRUE
);
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);
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
;
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.;
277 (g_object_get_data (G_OBJECT (so
), "pt-width-at-copy"));
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"));
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
);
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
);
312 sog_cb_save_as (SheetObject
*so
, SheetControl
*sc
)
319 GOImageFormat selected_format
;
320 GOImageFormatInfo
const *format_info
;
321 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
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
);
335 output
= go_file_create (uri
, &err
);
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
);
343 go_cmd_context_error (GO_CMD_CONTEXT (wbcg
), err
);
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
);
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
),
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
);
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
}
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
));
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
,
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
);
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
);
441 gnm_sogg_prep_sax_parser (so
, xin
, attrs
, convs
);
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
);
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
,
463 WorkbookControl
*wbc
;
464 } gnm_sog_user_config_t
;
467 gnm_sog_user_config_free_data (gpointer data
,
471 closure
->data
= NULL
;
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
));
482 gnm_sog_user_config (SheetObject
*so
, SheetControl
*sc
)
484 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
486 gnm_sog_user_config_t
*data
;
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);
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
);
506 gnm_sog_foreach_dep (SheetObject
*so
,
507 SheetObjectForeachDepFunc func
,
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
);
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
);
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
);
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);
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
) {
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]));
554 /*FIXME: get dimensions from print settings */
555 gog_graph_set_size (sog
->graph
, 400., 300.);
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
;
588 gnm_sog_init (GObject
*obj
)
590 SheetObject
*so
= GNM_SO (obj
);
591 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_DOWN_RIGHT
;
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
;
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.
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")
628 sheet_object_graph_set_gog (sog
, graph
);
630 sog
->anchor
.mode
= data
->anchor_mode
;
636 * sheet_object_graph_get_gog:
639 * Returns: (transfer none): the embedded #GogGraph or %NULL on error.
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.
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
));
667 if (sog
->graph
== graph
)
670 g_object_ref (graph
);
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
);
683 if (so
->sheet
!= NULL
)
684 sog_datas_set_sheet (sog
, so
->sheet
);
685 sog
->add_sig
= g_signal_connect_object (G_OBJECT (graph
),
687 G_CALLBACK (cb_graph_add_data
), G_OBJECT (sog
), 0);
688 sog
->remove_sig
= g_signal_connect_object (G_OBJECT (graph
),
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
);
695 sog
->renderer
= gog_renderer_new (sog
->graph
);
699 cb_graph_guru_done (WBCGtk
*wbcg
)
701 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
705 cb_graph_data_closure_done (GnmGraphDataClosure
*data
)
708 g_object_set_data (data
->obj
,"data-closure", NULL
);
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
);
718 GogObject
*gobj
= gog_object_get_child_by_name (graph
, "Chart");
719 gobj
= gog_object_get_child_by_name (gobj
, "Plot");
722 gog_plot_clear_series (GOG_PLOT (gobj
));
723 gog_data_allocator_allocate (data
->dalloc
, GOG_PLOT (gobj
));
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
);
733 GogObject
*gobj
= gog_object_get_child_by_name (graph
, "Chart");
734 gobj
= gog_object_get_child_by_name (gobj
, "Plot");
737 gog_plot_clear_series (GOG_PLOT (gobj
));
738 gog_data_allocator_allocate (data
->dalloc
, GOG_PLOT (gobj
));
743 cb_sheet_target_changed (GtkToggleButton
*btn
, GnmGraphDataClosure
*data
)
745 data
->new_sheet
= gtk_toggle_button_get_active (btn
);
749 sheet_object_graph_guru (WBCGtk
*wbcg
, GogGraph
*graph
,
752 GtkWidget
*dialog
= gog_guru (graph
, GOG_DATA_ALLOCATOR (wbcg
),
753 GO_CMD_CONTEXT (wbcg
), closure
);
755 GnmGraphDataClosure
*data
= (GnmGraphDataClosure
*) g_new0 (GnmGraphDataClosure
, 1);
756 GtkWidget
*custom
= gtk_grid_new (), *w
;
759 data
->dalloc
= GOG_DATA_ALLOCATOR (wbcg
);
760 g_object_set (custom
,
762 "column-spacing", 12,
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");
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
),
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:
807 * Updates the size of the graph item in the canvas for graph sheets objects.
810 sheet_object_graph_ensure_size (SheetObject
*sog
)
812 GList
*ptr
= sog
->realized_list
;
814 cb_post_new_view (GOC_ITEM (GOC_GROUP (ptr
->data
)->children
->data
));
819 /*****************************************************************************/
820 /* Support for Guppi graphs */
823 GnmConventions
const *convs
;
830 unsigned cur_index
, max_data
;
834 vector_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
836 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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
);
848 vector_end (GsfXMLIn
*xin
, G_GNUC_UNUSED GsfXMLBlob
*unknown
)
850 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
852 if (state
->cur_index
>= state
->max_data
)
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
;
860 plot_type_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
862 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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"))
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
,
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
,
887 gog_object_add_by_name (state
->chart
, "Backplane", NULL
);
890 gog_object_add_by_name (GOG_OBJECT (state
->chart
), "Plot", GOG_OBJECT (state
->plot
));
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
);
902 position_end (GsfXMLIn
*xin
, G_GNUC_UNUSED GsfXMLBlob
*unknown
)
904 GuppiReadState
*state
= (GuppiReadState
*)xin
->user_state
;
905 if (!xin
->content
->str
)
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
);
918 series_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
920 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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]);
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,
942 dim_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
944 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
946 char const *name
= "?";
947 GogMSDimType type
= GOG_MS_DIM_LABELS
;
948 GogPlotDesc
const *desc
= gog_plot_description (state
->plot
);
950 for (i
= 0; attrs
!= NULL
&& attrs
[i
] && attrs
[i
+1] ; i
+= 2)
951 if (0 == strcmp (attrs
[i
], "dim_name"))
953 else if (0 == strcmp (attrs
[i
], "ID"))
954 id
= strtoul (attrs
[i
+1], NULL
, 10);
956 id
>= state
->data
->len
|| g_ptr_array_index (state
->data
, id
) == NULL
)
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
,
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
);
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
,
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
,
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
);
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
);
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
);
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.,
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
);
1050 gnm_sogg_sax_parser_done (G_GNUC_UNUSED GsfXMLIn
*xin
, GuppiReadState
*state
)
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
);
1058 g_object_unref (obj
);
1060 g_ptr_array_free (state
->data
, TRUE
);
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
),
1104 static GsfXMLInDoc
*doc
= NULL
;
1105 GuppiReadState
*state
;
1106 GogTheme
*theme
= gog_theme_registry_lookup ("Guppi");
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
);