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
22 #include <gnumeric-config.h>
23 #include <glib/gi18n-lib.h>
25 #include <sheet-object-graph.h>
27 #include <gnm-pane-impl.h>
28 #include <sheet-control-gui.h>
31 #include <gnm-graph-window.h>
32 #include <style-color.h>
33 #include <sheet-object-impl.h>
36 #include <application.h>
38 #include <print-info.h>
40 #include <workbook-view.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>
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
);
62 goc_item_set (GOC_ITEM (sov
),
63 "x", MIN (coords
[0], coords
[2]) / scale
,
64 "y", MIN (coords
[3], coords
[1]) / scale
,
67 "width", (fabs (coords
[2] - coords
[0]) + 1.) / scale
,
68 "height", (fabs (coords
[3] - coords
[1]) + 1.) / scale
,
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
,
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))
97 GogRenderer
*renderer
;
98 gulong add_sig
, remove_sig
;
100 typedef SheetObjectClass SheetObjectGraphClass
;
102 static GObjectClass
*parent_klass
;
105 sog_data_set_sheet (G_GNUC_UNUSED SheetObjectGraph
*sog
,
106 GOData
*data
, Sheet
*sheet
)
108 gnm_go_data_set_sheet (data
, sheet
);
112 sog_data_foreach_dep (SheetObject
*so
, GOData
*data
,
113 SheetObjectForeachDepFunc func
, gpointer user
)
115 gnm_go_data_foreach_dep (data
, so
, func
, user
);
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
);
125 cb_graph_remove_data (G_GNUC_UNUSED GogGraph
*graph
,
126 GOData
*data
, SheetObjectGraph
*sog
)
128 sog_data_set_sheet (sog
, data
, NULL
);
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
);
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
);
154 parent_klass
->finalize (obj
);
158 cb_graph_size_changed (GocItem
*item
, GtkAllocation
*allocation
)
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
);
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.;
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
);
188 cb_post_new_view (GocItem
*item
)
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
);
197 static SheetObjectView
*
198 gnm_sog_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
201 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
202 if (GNM_IS_PANE (container
)) {
204 pane
= GNM_PANE (container
);
205 view
= goc_item_new (pane
->object_views
,
206 so_graph_goc_view_get_type (),
208 goc_item_new (GOC_GROUP (view
),
210 "renderer", sog
->renderer
,
212 return gnm_pane_object_register (so
, view
, TRUE
);
214 GocCanvas
*canvas
= GOC_CANVAS (container
);
215 GocItem
*view
= goc_item_new (goc_canvas_get_root (canvas
),
216 so_graph_goc_view_get_type (),
218 GocItem
*item
= goc_item_new (GOC_GROUP (view
),
220 "renderer", sog
->renderer
,
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");
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
),
244 g_slist_free_full (mimes
, g_free
);
245 /* No need to eliminate duplicates. */
246 gtk_target_list_add_image_targets (tl
, 0, TRUE
);
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);
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
;
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.;
275 (g_object_get_data (G_OBJECT (so
), "pt-width-at-copy"));
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"));
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
);
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
);
310 sog_cb_save_as (SheetObject
*so
, SheetControl
*sc
)
317 GOImageFormat selected_format
;
318 GOImageFormatInfo
const *format_info
;
319 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
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
);
333 output
= go_file_create (uri
, &err
);
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
);
341 go_cmd_context_error (GO_CMD_CONTEXT (wbcg
), err
);
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
);
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
),
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
);
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
}
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
));
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
,
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
);
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
);
439 gnm_sogg_prep_sax_parser (so
, xin
, attrs
, convs
);
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
);
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
,
461 WorkbookControl
*wbc
;
462 } gnm_sog_user_config_t
;
465 gnm_sog_user_config_free_data (gpointer data
,
469 closure
->data
= NULL
;
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
));
480 gnm_sog_user_config (SheetObject
*so
, SheetControl
*sc
)
482 SheetObjectGraph
*sog
= GNM_SO_GRAPH (so
);
484 gnm_sog_user_config_t
*data
;
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);
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
);
504 gnm_sog_foreach_dep (SheetObject
*so
,
505 SheetObjectForeachDepFunc func
,
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
);
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
);
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
);
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);
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
) {
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]));
552 /*FIXME: get dimensions from print settings */
553 gog_graph_set_size (sog
->graph
, 400., 300.);
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
;
586 gnm_sog_init (GObject
*obj
)
588 SheetObject
*so
= GNM_SO (obj
);
589 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_DOWN_RIGHT
;
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
;
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.
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")
626 sheet_object_graph_set_gog (sog
, graph
);
628 sog
->anchor
.mode
= data
->anchor_mode
;
634 * sheet_object_graph_get_gog:
637 * Returns: (transfer none): the embedded #GogGraph or %NULL on error.
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.
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
));
665 if (sog
->graph
== graph
)
668 g_object_ref (graph
);
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
);
681 if (so
->sheet
!= NULL
)
682 sog_datas_set_sheet (sog
, so
->sheet
);
683 sog
->add_sig
= g_signal_connect_object (G_OBJECT (graph
),
685 G_CALLBACK (cb_graph_add_data
), G_OBJECT (sog
), 0);
686 sog
->remove_sig
= g_signal_connect_object (G_OBJECT (graph
),
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
);
693 sog
->renderer
= gog_renderer_new (sog
->graph
);
697 cb_graph_guru_done (WBCGtk
*wbcg
)
699 wbcg_edit_finish (wbcg
, WBC_EDIT_REJECT
, NULL
);
703 cb_graph_data_closure_done (GnmGraphDataClosure
*data
)
706 g_object_set_data (data
->obj
,"data-closure", NULL
);
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
);
716 GogObject
*gobj
= gog_object_get_child_by_name (graph
, "Chart");
717 gobj
= gog_object_get_child_by_name (gobj
, "Plot");
720 gog_plot_clear_series (GOG_PLOT (gobj
));
721 gog_data_allocator_allocate (data
->dalloc
, GOG_PLOT (gobj
));
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
);
731 GogObject
*gobj
= gog_object_get_child_by_name (graph
, "Chart");
732 gobj
= gog_object_get_child_by_name (gobj
, "Plot");
735 gog_plot_clear_series (GOG_PLOT (gobj
));
736 gog_data_allocator_allocate (data
->dalloc
, GOG_PLOT (gobj
));
741 cb_sheet_target_changed (GtkToggleButton
*btn
, GnmGraphDataClosure
*data
)
743 data
->new_sheet
= gtk_toggle_button_get_active (btn
);
747 sheet_object_graph_guru (WBCGtk
*wbcg
, GogGraph
*graph
,
750 GtkWidget
*dialog
= gog_guru (graph
, GOG_DATA_ALLOCATOR (wbcg
),
751 GO_CMD_CONTEXT (wbcg
), closure
);
753 GnmGraphDataClosure
*data
= (GnmGraphDataClosure
*) g_new0 (GnmGraphDataClosure
, 1);
754 GtkWidget
*custom
= gtk_grid_new (), *w
;
757 data
->dalloc
= GOG_DATA_ALLOCATOR (wbcg
);
758 g_object_set (custom
,
760 "column-spacing", 12,
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");
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
),
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:
805 * Updates the size of the graph item in the canvas for graph sheets objects.
808 sheet_object_graph_ensure_size (SheetObject
*sog
)
810 GList
*ptr
= sog
->realized_list
;
812 cb_post_new_view (GOC_ITEM (GOC_GROUP (ptr
->data
)->children
->data
));
817 /*****************************************************************************/
818 /* Support for Guppi graphs */
821 GnmConventions
const *convs
;
828 unsigned cur_index
, max_data
;
832 vector_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
834 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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
);
846 vector_end (GsfXMLIn
*xin
, G_GNUC_UNUSED GsfXMLBlob
*unknown
)
848 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
850 if (state
->cur_index
>= state
->max_data
)
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
;
858 plot_type_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
860 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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"))
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
,
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
,
885 gog_object_add_by_name (state
->chart
, "Backplane", NULL
);
888 gog_object_add_by_name (GOG_OBJECT (state
->chart
), "Plot", GOG_OBJECT (state
->plot
));
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
);
900 position_end (GsfXMLIn
*xin
, G_GNUC_UNUSED GsfXMLBlob
*unknown
)
902 GuppiReadState
*state
= (GuppiReadState
*)xin
->user_state
;
903 if (!xin
->content
->str
)
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
);
916 series_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
918 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
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]);
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,
940 dim_start (GsfXMLIn
*xin
, xmlChar
const **attrs
)
942 GuppiReadState
*state
= (GuppiReadState
*) xin
->user_state
;
944 char const *name
= "?";
945 GogMSDimType type
= GOG_MS_DIM_LABELS
;
946 GogPlotDesc
const *desc
= gog_plot_description (state
->plot
);
948 for (i
= 0; attrs
!= NULL
&& attrs
[i
] && attrs
[i
+1] ; i
+= 2)
949 if (0 == strcmp (attrs
[i
], "dim_name"))
951 else if (0 == strcmp (attrs
[i
], "ID"))
952 id
= strtoul (attrs
[i
+1], NULL
, 10);
954 id
>= state
->data
->len
|| g_ptr_array_index (state
->data
, id
) == NULL
)
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
,
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
);
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
,
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
,
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
);
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
);
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
);
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.,
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
);
1048 gnm_sogg_sax_parser_done (G_GNUC_UNUSED GsfXMLIn
*xin
, GuppiReadState
*state
)
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
);
1056 g_object_unref (obj
);
1058 g_ptr_array_free (state
->data
, TRUE
);
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
),
1102 static GsfXMLInDoc
*doc
= NULL
;
1103 GuppiReadState
*state
;
1104 GogTheme
*theme
= gog_theme_registry_lookup ("Guppi");
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
);