1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * sheet-object-component.c
5 * Copyright (C) 2008 Jean Bréfort <jean.brefort@normalesup.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) any later version.
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, see <https://www.gnu.org/licenses/>
21 #include <gnumeric-config.h>
23 #include "application.h"
25 #include "gnm-pane-impl.h"
27 #include "sheet-control-gui.h"
28 #include "sheet-object-component.h"
29 #include "sheet-object-impl.h"
31 #include <goffice/goffice.h>
32 #include <goffice/component/go-component.h>
33 #include <gsf/gsf-impl-utils.h>
34 #include <gsf/gsf-output-gio.h>
35 #include <glib/gi18n-lib.h>
39 so_component_view_set_bounds (SheetObjectView
*sov
, double const *coords
, gboolean visible
)
41 GocItem
*view
= GOC_ITEM (GOC_GROUP (sov
)->children
->data
);
42 double scale
= goc_canvas_get_pixels_per_unit (view
->canvas
);
45 GOComponent
*component
= sheet_object_component_get_component (sheet_object_view_get_so (sov
));
47 goc_item_set (GOC_ITEM (sov
),
48 "x", MIN (coords
[0], coords
[2]) / scale
,
49 "y", MIN (coords
[3], coords
[1]) / scale
,
51 if (component
&& ! go_component_is_resizable (component
)) {
52 go_component_get_size (component
, &width
, &height
);
55 "width", width
* gnm_app_display_dpi_get (TRUE
),
56 "height", height
* gnm_app_display_dpi_get (FALSE
),
60 "width", (fabs (coords
[2] - coords
[0]) + 1.) / scale
,
61 "height", (fabs (coords
[3] - coords
[1]) + 1.) / scale
,
69 typedef SheetObjectView SOComponentGocView
;
70 typedef SheetObjectViewClass SOComponentGocViewClass
;
73 so_component_goc_view_class_init (SheetObjectViewClass
*sov_klass
)
75 sov_klass
->set_bounds
= so_component_view_set_bounds
;
78 static GSF_CLASS (SOComponentGocView
, so_component_goc_view
,
79 so_component_goc_view_class_init
, NULL
,
83 /****************************************************************************/
84 #define SHEET_OBJECT_CONFIG_KEY "sheet-object-component-key"
86 #define SHEET_OBJECT_COMPONENT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GNM_SO_COMPONENT_TYPE, SheetObjectComponentClass))
90 GOComponent
*component
;
92 } SheetObjectComponent
;
93 typedef SheetObjectClass SheetObjectComponentClass
;
95 static GObjectClass
*parent_klass
;
98 gnm_soc_finalize (GObject
*obj
)
100 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (obj
);
102 g_object_unref (soc
->component
);
104 parent_klass
->finalize (obj
);
107 static SheetObjectView
*
108 gnm_soc_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
110 GnmPane
*pane
= GNM_PANE (container
);
111 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
112 GocItem
*view
= goc_item_new (pane
->object_views
,
113 so_component_goc_view_get_type (),
115 goc_item_hide (goc_item_new (GOC_GROUP (view
),
117 "object", soc
->component
,
119 return gnm_pane_object_register (so
, view
, TRUE
);
122 static GtkTargetList
*
123 gnm_soc_get_target_list (SheetObject
const *so
)
125 GtkTargetList
*tl
= gtk_target_list_new (NULL
, 0);
126 char *mime_str
= go_image_format_to_mime ("svg");
129 mimes
= go_strsplit_to_slist (mime_str
, ',');
130 for (ptr
= mimes
; ptr
!= NULL
; ptr
= ptr
->next
) {
131 const char *mime
= ptr
->data
;
133 if (mime
!= NULL
&& *mime
!= '\0')
134 gtk_target_list_add (tl
, gdk_atom_intern (mime
, FALSE
),
138 g_slist_free_full (mimes
, g_free
);
139 /* No need to eliminate duplicates. */
140 gtk_target_list_add_image_targets (tl
, 0, TRUE
);
145 static GtkTargetList
*
146 gnm_soc_get_object_target_list (SheetObject
const *so
)
148 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
149 GtkTargetList
*tl
= gtk_target_list_new (NULL
, 0);
150 if (soc
&& soc
->component
)
151 gtk_target_list_add (tl
,
152 gdk_atom_intern (go_component_get_mime_type (soc
->component
), FALSE
), 0, 0);
157 gnm_soc_write_image (SheetObject
const *so
, char const *format
, double resolution
,
158 GsfOutput
*output
, GError
**err
)
160 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
161 gboolean res
= FALSE
;
166 sheet_object_position_pts_get (GNM_SO (so
), coords
);
167 w
= fabs (coords
[2] - coords
[0]) + 1.;
168 h
= fabs (coords
[3] - coords
[1]) + 1.;
171 (g_object_get_data (G_OBJECT (so
), "pt-width-at-copy"));
173 (g_object_get_data (G_OBJECT (so
), "pt-height-at-copy"));
176 g_return_if_fail (w
> 0 && h
> 0);
178 res
= go_component_export_image (soc
->component
, go_image_get_format_from_name (format
),
179 output
, resolution
, resolution
);
181 if (!res
&& err
&& *err
== NULL
)
182 *err
= g_error_new (gsf_output_error_id (), 0,
183 _("Unknown failure while saving image"));
187 soc_cb_save_as (SheetObject
*so
, SheetControl
*sc
)
189 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
190 /* FIXME: This violates model gui barrier */
191 WBCGtk
*wbcg
= scg_wbcg (GNM_SCG (sc
));
192 GtkWidget
*dlg
= gtk_file_chooser_dialog_new (_("Save as"),
193 GTK_WINDOW (wbcg_toplevel (wbcg
)),
194 GTK_FILE_CHOOSER_ACTION_SAVE
,
195 GNM_STOCK_SAVE
, GTK_RESPONSE_ACCEPT
,
196 GNM_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
198 GtkFileFilter
*filter
= gtk_file_filter_new ();
199 gtk_file_filter_add_mime_type (filter
, go_component_get_mime_type (soc
->component
));
200 gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (dlg
), filter
);
201 if (gtk_dialog_run (GTK_DIALOG (dlg
)) == GTK_RESPONSE_ACCEPT
) {
202 char *uri
= gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dlg
));
204 GsfOutput
*output
= gsf_output_gio_new_for_uri (uri
, &err
);
206 go_cmd_context_error (GO_CMD_CONTEXT (wbcg
), err
);
210 gpointer user_data
= NULL
;
211 void (*clearfunc
) (gpointer
) = NULL
;
212 go_component_get_data (soc
->component
, (gpointer
) &buf
, &length
, &clearfunc
, &user_data
);
213 gsf_output_write (output
, length
, buf
);
215 clearfunc ((user_data
)? user_data
: buf
);
216 gsf_output_close (output
);
217 g_object_unref (output
);
221 gtk_widget_destroy (dlg
);
225 soc_cb_save_as_image (SheetObject
*so
, SheetControl
*sc
)
232 GOImageFormat selected_format
;
233 GOImageFormatInfo
const *format_info
;
234 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
237 g_return_if_fail (soc
!= NULL
);
239 /* assuming that components support the same image formats than graphs */
240 l
= gog_graph_get_supported_image_formats ();
241 g_return_if_fail (l
!= NULL
);
242 selected_format
= GPOINTER_TO_UINT (l
->data
);
244 /* FIXME: This violates model gui barrier */
245 wbcg
= scg_wbcg (GNM_SCG (sc
));
246 uri
= go_gui_get_image_save_info (wbcg_toplevel (wbcg
), l
, &selected_format
, &resolution
);
249 output
= go_file_create (uri
, &err
);
252 format_info
= go_image_get_format_info (selected_format
);
253 sheet_object_write_image (so
, format_info
->name
, resolution
, output
, &err
);
254 g_object_unref (output
);
257 go_cmd_context_error (GO_CMD_CONTEXT (wbcg
), err
);
265 gnm_soc_populate_menu (SheetObject
*so
, GPtrArray
*actions
)
267 static SheetObjectAction
const soc_actions
[] = {
268 { "document-save", GNM_N_STOCK_SAVE
, NULL
, 0, soc_cb_save_as
},
269 { "document-save-as", N_("_Save As Image"), NULL
, 0, soc_cb_save_as_image
}
274 GNM_SO_CLASS (parent_klass
)->populate_menu (so
, actions
);
276 for (i
= 0; i
< G_N_ELEMENTS (soc_actions
); i
++)
277 go_ptr_array_insert (actions
, (gpointer
) (soc_actions
+ i
), 1 + i
);
281 gnm_soc_write_object (SheetObject
const *so
, char const *format
,
282 GsfOutput
*output
, GError
**err
,
283 GnmConventions
const *convs
)
285 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
288 gpointer user_data
= NULL
;
289 void (*clearfunc
) (gpointer
) = NULL
;
290 go_component_get_data (soc
->component
, (gpointer
) &buf
, &length
, &clearfunc
, &user_data
);
291 gsf_output_write (output
, length
, buf
);
293 clearfunc ((user_data
)? user_data
: buf
);
297 gnm_soc_write_xml_sax (SheetObject
const *so
, GsfXMLOut
*output
,
298 GnmConventions
const *convs
)
300 SheetObjectComponent
const *soc
= GNM_SO_COMPONENT (so
);
301 go_component_write_xml_sax (soc
->component
, output
);
305 soc_xml_finish (GOComponent
*component
, SheetObject
*so
)
307 sheet_object_component_set_component (so
, component
);
311 gnm_soc_prep_sax_parser (SheetObject
*so
, GsfXMLIn
*xin
, xmlChar
const **attrs
,
312 GnmConventions
const *convs
)
314 go_component_sax_push_parser (xin
, attrs
,
315 (GOComponentSaxHandler
) soc_xml_finish
, so
);
319 gnm_soc_copy (SheetObject
*dst
, SheetObject
const *src
)
321 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (src
);
322 GOComponent
*component
= go_component_duplicate (soc
->component
);
323 sheet_object_component_set_component (dst
, component
);
324 g_object_unref (component
);
328 gnm_soc_default_size (SheetObject
const *so
, double *w
, double *h
)
330 SheetObjectComponent
const *soc
= GNM_SO_COMPONENT (so
);
331 if (soc
->component
&& !go_component_is_resizable (soc
->component
)) {
332 go_component_get_size (soc
->component
, w
, h
);
333 *w
= GO_IN_TO_PT (*w
);
334 *h
= GO_IN_TO_PT (*h
);
336 *w
= GO_CM_TO_PT ((double)5);
337 *h
= GO_CM_TO_PT ((double)5);
343 WorkbookControl
*wbc
;
344 GOComponent
*component
;
346 } gnm_soc_user_config_t
;
349 component_changed_cb (GOComponent
*component
, gnm_soc_user_config_t
*data
)
351 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (data
->so
);
352 cmd_so_component_config (data
->wbc
, data
->so
, G_OBJECT (component
), G_OBJECT (soc
->component
));
356 destroy_cb ( gnm_soc_user_config_t
*data
)
358 wbcg_edit_finish (WBC_GTK (data
->wbc
), WBC_EDIT_REJECT
, NULL
);
359 go_component_set_command_context (data
->component
, NULL
);
360 g_object_unref (data
->component
);
365 gnm_soc_user_config (SheetObject
*so
, SheetControl
*sc
)
367 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
369 GOComponent
*new_comp
;
371 g_return_if_fail (soc
&& soc
->component
);
373 go_component_set_command_context (soc
->component
, GO_CMD_CONTEXT (scg_wbcg (GNM_SCG (sc
))));
374 new_comp
= go_component_duplicate (soc
->component
);
375 go_component_set_command_context (new_comp
, GO_CMD_CONTEXT (scg_wbcg (GNM_SCG (sc
))));
376 w
= (GtkWidget
*) go_component_edit (new_comp
);
377 go_component_set_command_context (soc
->component
, NULL
);
379 gnm_soc_user_config_t
*data
= g_new0 (gnm_soc_user_config_t
, 1);
381 data
->component
= new_comp
;
382 data
->wbc
= GNM_WBC (scg_wbcg (GNM_SCG (sc
)));
383 data
->signal
= g_signal_connect (new_comp
, "changed", G_CALLBACK (component_changed_cb
), data
);
384 g_object_set_data_full (G_OBJECT (w
), "editor", data
, (GDestroyNotify
) destroy_cb
);
385 wbc_gtk_attach_guru (scg_wbcg (GNM_SCG (sc
)), w
);
390 gnm_soc_draw_cairo (SheetObject
const *so
, cairo_t
*cr
,
391 double width
, double height
)
393 SheetObjectComponent
*soc
= GNM_SO_COMPONENT (so
);
394 g_return_if_fail (soc
&& soc
->component
);
396 go_component_render (soc
->component
, cr
, width
, height
);
400 gnm_soc_class_init (GObjectClass
*klass
)
402 SheetObjectClass
*so_class
= GNM_SO_CLASS (klass
);
404 parent_klass
= g_type_class_peek_parent (klass
);
406 /* Object class method overrides */
407 klass
->finalize
= gnm_soc_finalize
;
409 /* SheetObject class method overrides */
410 so_class
->new_view
= gnm_soc_new_view
;
411 so_class
->populate_menu
= gnm_soc_populate_menu
;
412 so_class
->write_xml_sax
= gnm_soc_write_xml_sax
;
413 so_class
->prep_sax_parser
= gnm_soc_prep_sax_parser
;
414 so_class
->copy
= gnm_soc_copy
;
415 so_class
->user_config
= gnm_soc_user_config
;
416 so_class
->default_size
= gnm_soc_default_size
;
417 so_class
->draw_cairo
= gnm_soc_draw_cairo
;
419 so_class
->rubber_band_directly
= FALSE
;
423 gnm_soc_init (GObject
*obj
)
425 SheetObject
*so
= GNM_SO (obj
);
426 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_DOWN_RIGHT
;
430 soc_imageable_init (SheetObjectImageableIface
*soi_iface
)
432 soi_iface
->get_target_list
= gnm_soc_get_target_list
;
433 soi_iface
->write_image
= gnm_soc_write_image
;
437 soc_exportable_init (SheetObjectExportableIface
*soe_iface
)
439 soe_iface
->get_target_list
= gnm_soc_get_object_target_list
;
440 soe_iface
->write_object
= gnm_soc_write_object
;
443 GSF_CLASS_FULL (SheetObjectComponent
, sheet_object_component
,
444 NULL
, NULL
, gnm_soc_class_init
,NULL
,
445 gnm_soc_init
, GNM_SO_TYPE
, 0,
446 GSF_INTERFACE (soc_imageable_init
, GNM_SO_IMAGEABLE_TYPE
) \
447 GSF_INTERFACE (soc_exportable_init
, GNM_SO_EXPORTABLE_TYPE
));
450 * sheet_object_component_new:
451 * @component: #GOComponent
453 * Returns: (transfer full): the newly allocated #SheetObject.
456 sheet_object_component_new (GOComponent
*component
)
458 SheetObjectComponent
*soc
= g_object_new (GNM_SO_COMPONENT_TYPE
, NULL
);
459 sheet_object_component_set_component (GNM_SO (soc
), component
);
464 * sheet_object_component_get_component:
467 * Returns: (transfer none): the embedded #GOComponent or %NULL on error.
470 sheet_object_component_get_component (SheetObject
*soc
)
472 g_return_val_if_fail (GNM_IS_SO_COMPONENT (soc
), NULL
);
474 return ((SheetObjectComponent
*) soc
)->component
;
478 sheet_object_component_set_component (SheetObject
*so
, GOComponent
*component
)
480 SheetObjectComponent
*soc
;
481 GList
*l
= so
->realized_list
;
483 g_return_if_fail (GNM_IS_SO_COMPONENT (so
));
484 soc
= GNM_SO_COMPONENT (so
);
485 if (soc
->component
!= NULL
) {
486 go_component_stop_editing (soc
->component
);
487 g_object_unref (soc
->component
);
490 soc
->component
= component
;
492 for (; l
; l
= l
->next
)
494 GocGroup
*group
= GOC_GROUP (l
->data
);
495 if (group
->children
->data
)
496 g_object_set (group
->children
->data
, "object", component
, NULL
);
499 g_object_ref (component
);
500 go_component_stop_editing (component
);
501 if (go_component_is_resizable (component
))
502 so
->flags
|= SHEET_OBJECT_CAN_RESIZE
;
504 so
->flags
&= ~(SHEET_OBJECT_CAN_RESIZE
| SHEET_OBJECT_SIZE_WITH_CELLS
);
505 so
->anchor
.mode
= GNM_SO_ANCHOR_ONE_CELL
;
507 if (go_component_is_editable (component
))
508 so
->flags
|= SHEET_OBJECT_CAN_EDIT
;
510 so
->flags
&= ~SHEET_OBJECT_CAN_EDIT
;