Use application properties instead of ugly dual-use global variable
[gnumeric.git] / src / sheet-object-component.c
blobdb832fe5d24b8bc1cce6b07999453323f2e0a108
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
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>
22 #include "gnumeric.h"
23 #include "application.h"
24 #include "commands.h"
25 #include "gnm-pane-impl.h"
26 #include "gui-util.h"
27 #include "sheet-control-gui.h"
28 #include "sheet-object-component.h"
29 #include "sheet-object-impl.h"
30 #include "wbc-gtk.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>
38 static void
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);
44 if (visible) {
45 GOComponent *component = sheet_object_component_get_component (sheet_object_view_get_so (sov));
46 double width, height;
47 goc_item_set (GOC_ITEM (sov),
48 "x", MIN (coords [0], coords[2]) / scale,
49 "y", MIN (coords [3], coords[1]) / scale,
50 NULL);
51 if (component && ! go_component_is_resizable (component)) {
52 go_component_get_size (component, &width, &height);
54 goc_item_set (view,
55 "width", width * gnm_app_display_dpi_get (TRUE),
56 "height", height * gnm_app_display_dpi_get (FALSE),
57 NULL);
58 } else
59 goc_item_set (view,
60 "width", (fabs (coords [2] - coords [0]) + 1.) / scale,
61 "height", (fabs (coords [3] - coords [1]) + 1.) / scale,
62 NULL);
64 goc_item_show (view);
65 } else
66 goc_item_hide (view);
69 typedef SheetObjectView SOComponentGocView;
70 typedef SheetObjectViewClass SOComponentGocViewClass;
72 static void
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,
80 GNM_SO_VIEW_TYPE)
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))
88 typedef struct {
89 SheetObject base;
90 GOComponent *component;
91 guint changed_signal;
92 } SheetObjectComponent;
93 typedef SheetObjectClass SheetObjectComponentClass;
95 static GObjectClass *parent_klass;
97 static void
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 (),
114 NULL);
115 goc_item_hide (goc_item_new (GOC_GROUP (view),
116 GOC_TYPE_COMPONENT,
117 "object", soc->component,
118 NULL));
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");
127 GSList *mimes, *ptr;
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),
135 0, 0);
137 g_free (mime_str);
138 g_slist_free_full (mimes, g_free);
139 /* No need to eliminate duplicates. */
140 gtk_target_list_add_image_targets (tl, 0, TRUE);
142 return tl;
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);
153 return tl;
156 static void
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;
162 double coords[4];
163 double w, h;
165 if (so->sheet) {
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.;
169 } else {
170 w = GPOINTER_TO_UINT
171 (g_object_get_data (G_OBJECT (so), "pt-width-at-copy"));
172 h = GPOINTER_TO_UINT
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"));
186 static void
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,
197 NULL);
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));
203 GError *err = NULL;
204 GsfOutput *output = gsf_output_gio_new_for_uri (uri, &err);
205 if (err != NULL)
206 go_cmd_context_error (GO_CMD_CONTEXT (wbcg), err);
207 else {
208 char *buf;
209 int length;
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);
214 if (clearfunc)
215 clearfunc ((user_data)? user_data: buf);
216 gsf_output_close (output);
217 g_object_unref (output);
219 g_free (uri);
221 gtk_widget_destroy (dlg);
224 static void
225 soc_cb_save_as_image (SheetObject *so, SheetControl *sc)
227 WBCGtk *wbcg;
228 char *uri;
229 GError *err = NULL;
230 GsfOutput *output;
231 GSList *l;
232 GOImageFormat selected_format;
233 GOImageFormatInfo const *format_info;
234 SheetObjectComponent *soc = GNM_SO_COMPONENT (so);
235 double resolution;
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);
247 if (!uri)
248 goto out;
249 output = go_file_create (uri, &err);
250 if (!output)
251 goto out;
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);
256 if (err != NULL)
257 go_cmd_context_error (GO_CMD_CONTEXT (wbcg), err);
259 out:
260 g_free (uri);
261 g_slist_free (l);
264 static void
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 }
272 unsigned int i;
274 GNM_SO_CLASS (parent_klass)->populate_menu (so, actions);
276 for (i = 0; i < G_N_ELEMENTS (soc_actions); i++)
277 g_ptr_array_insert (actions, 1 + i, (gpointer)(soc_actions + i));
280 static void
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);
286 char *buf;
287 int length;
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);
292 if (clearfunc)
293 clearfunc ((user_data)? user_data: buf);
296 static void
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);
304 static void
305 soc_xml_finish (GOComponent *component, SheetObject *so)
307 sheet_object_component_set_component (so, component);
310 static void
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);
318 static void
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);
327 static void
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);
335 } else {
336 *w = GO_CM_TO_PT ((double)5);
337 *h = GO_CM_TO_PT ((double)5);
341 typedef struct {
342 SheetObject *so;
343 WorkbookControl *wbc;
344 GOComponent *component;
345 gulong signal;
346 } gnm_soc_user_config_t;
348 static void
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));
355 static void
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);
361 g_free (data);
364 static void
365 gnm_soc_user_config (SheetObject *so, SheetControl *sc)
367 SheetObjectComponent *soc = GNM_SO_COMPONENT (so);
368 GtkWidget *w;
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);
378 if (w) {
379 gnm_soc_user_config_t *data = g_new0 (gnm_soc_user_config_t, 1);
380 data->so = so;
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);
389 static void
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);
399 static void
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;
422 static void
423 gnm_soc_init (GObject *obj)
425 SheetObject *so = GNM_SO (obj);
426 so->anchor.base.direction = GOD_ANCHOR_DIR_DOWN_RIGHT;
429 static void
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;
436 static void
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.
455 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);
460 return GNM_SO (soc);
464 * sheet_object_component_get_component:
465 * @soc: #SheetObject
467 * Returns: (transfer none): the embedded #GOComponent or %NULL on error.
469 GOComponent*
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;
477 void
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)
493 if (l->data) {
494 GocGroup *group = GOC_GROUP (l->data);
495 if (group->children->data)
496 g_object_set (group->children->data, "object", component, NULL);
498 if (component) {
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;
503 else {
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;
509 else
510 so->flags &= ~SHEET_OBJECT_CAN_EDIT;