Updated Czech translation
[gnumeric.git] / src / sheet-object.c
blob0d3b536adb76ca91e21296ecd9462c70952aaa68
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 /*
4 * sheet-object.c: Implements the sheet object manipulation for Gnumeric
6 * Author:
7 * Miguel de Icaza (miguel@kernel.org)
8 * Michael Meeks (mmeeks@gnu.org)
9 * Jody Goldberg (jody@gnome.org)
11 #include <gnumeric-config.h>
12 #include <glib/gi18n-lib.h>
13 #include "gnumeric.h"
14 #include "sheet-object.h"
16 #include "sheet.h"
17 #include "dependent.h"
18 #include "sheet-view.h"
19 #include "sheet-control.h"
20 #include "sheet-control-gui.h"
21 #include "sheet-private.h"
22 #include "dialogs.h"
23 #include "sheet-object-impl.h"
24 #include "expr.h"
25 #include "ranges.h"
26 #include "commands.h"
27 #include "gui-util.h"
29 #include "gnm-pane-impl.h"
30 #include "gnm-so-line.h"
31 #include "gnm-so-filled.h"
32 #include "sheet-control-gui-priv.h"
33 #include "sheet-object-cell-comment.h"
34 #include "sheet-object-widget.h"
35 #include "sheet-object-graph.h"
36 #include "sheet-object-image.h"
37 #include "sheet-filter-combo.h"
38 #include "wbc-gtk-impl.h"
39 #include "graph.h"
40 #include "print.h"
41 #include <goffice/goffice.h>
42 #include "application.h"
43 #include "gutils.h"
45 #include <libxml/globals.h>
46 #include <gsf/gsf-impl-utils.h>
48 #include <string.h>
50 /* GType code for SheetObjectAnchor */
51 static SheetObjectAnchor *
52 sheet_object_anchor_copy (SheetObjectAnchor * soa)
54 SheetObjectAnchor *res = g_new (SheetObjectAnchor, 1);
55 *res = *soa;
56 return res;
59 GType
60 sheet_object_anchor_get_type (void)
62 static GType t = 0;
64 if (t == 0) {
65 t = g_boxed_type_register_static ("SheetObjectAnchor",
66 (GBoxedCopyFunc)sheet_object_anchor_copy,
67 (GBoxedFreeFunc)g_free);
69 return t;
72 /* Returns the class for a SheetObject */
73 #define SO_CLASS(so) SHEET_OBJECT_CLASS(G_OBJECT_GET_CLASS(so))
75 enum {
76 SO_PROP_0 = 0,
77 SO_PROP_NAME
80 enum {
81 BOUNDS_CHANGED,
82 UNREALIZED,
83 LAST_SIGNAL
85 static guint signals [LAST_SIGNAL] = { 0 };
86 static GObjectClass *parent_klass;
87 static GQuark sov_so_quark;
88 static GQuark sov_container_quark;
90 void
91 sheet_object_set_print_flag (SheetObject *so, gboolean *print)
93 if (*print)
94 so->flags = (so->flags | SHEET_OBJECT_PRINT);
95 else
96 so->flags = (so->flags & ~SHEET_OBJECT_PRINT);
100 static void
101 cb_so_size_position (SheetObject *so, SheetControl *sc)
103 WBCGtk *wbcg;
105 g_return_if_fail (IS_SHEET_CONTROL_GUI (sc));
107 wbcg = scg_wbcg ((SheetControlGUI *)sc);
109 if (wbcg->edit_line.guru != NULL) {
110 GtkWidget *w = wbcg->edit_line.guru;
111 wbc_gtk_detach_guru (wbcg);
112 gtk_widget_destroy (w);
115 dialog_so_size (wbcg, G_OBJECT (so));
118 static void
119 cb_so_snap_to_grid (SheetObject *so, SheetControl *sc)
121 SheetObjectAnchor *snapped =
122 sheet_object_anchor_dup (sheet_object_get_anchor (so));
123 snapped->offset[0] = snapped->offset[1] = 0.;
124 snapped->offset[2] = snapped->offset[3] = 1.;
125 cmd_objects_move (sc_wbc (sc),
126 g_slist_prepend (NULL, so),
127 g_slist_prepend (NULL, snapped),
128 FALSE, _("Snap object to grid"));
130 static void
131 cb_so_pull_to_front (SheetObject *so, SheetControl *sc)
133 cmd_object_raise (sc_wbc (sc), so, cmd_object_pull_to_front);
135 static void
136 cb_so_pull_forward (SheetObject *so, SheetControl *sc)
138 cmd_object_raise (sc_wbc (sc), so, cmd_object_pull_forward);
140 static void
141 cb_so_push_backward (SheetObject *so, SheetControl *sc)
143 cmd_object_raise (sc_wbc (sc), so, cmd_object_push_backward);
145 static void
146 cb_so_push_to_back (SheetObject *so, SheetControl *sc)
148 cmd_object_raise (sc_wbc (sc), so, cmd_object_push_to_back);
150 static void
151 cb_so_delete (SheetObject *so, SheetControl *sc)
153 cmd_objects_delete (sc_wbc (sc),
154 go_slist_create (so, NULL), NULL);
156 static void
157 cb_so_print (SheetObject *so, SheetControl *sc)
159 GPtrArray *a = g_ptr_array_new ();
160 g_ptr_array_add (a, so);
161 gnm_print_so (sc_wbc (sc), a, NULL);
162 g_ptr_array_unref (a);
164 void
165 sheet_object_get_editor (SheetObject *so, SheetControl *sc)
167 WBCGtk *wbcg;
169 g_return_if_fail (IS_SHEET_OBJECT (so));
170 g_return_if_fail (SO_CLASS (so));
171 g_return_if_fail (IS_SHEET_CONTROL_GUI (sc));
173 wbcg = scg_wbcg ((SheetControlGUI *)sc);
175 if (wbcg->edit_line.guru != NULL) {
176 GtkWidget *w = wbcg->edit_line.guru;
177 wbc_gtk_detach_guru (wbcg);
178 gtk_widget_destroy (w);
181 if (SO_CLASS(so)->user_config)
182 SO_CLASS(so)->user_config (so, sc);
184 static void
185 cb_so_cut (SheetObject *so, SheetControl *sc)
187 gnm_app_clipboard_cut_copy_obj (sc_wbc (sc), TRUE, sc_view (sc),
188 go_slist_create (so, NULL));
190 static void
191 cb_so_copy (SheetObject *so, SheetControl *sc)
193 gnm_app_clipboard_cut_copy_obj (sc_wbc (sc), FALSE, sc_view (sc),
194 go_slist_create (so, NULL));
197 gboolean
198 sheet_object_can_print (SheetObject const *so)
200 g_return_val_if_fail (IS_SHEET_OBJECT (so), FALSE);
201 return (so->flags & SHEET_OBJECT_IS_VISIBLE) &&
202 (so->flags & SHEET_OBJECT_PRINT) &&
203 SO_CLASS (so)->draw_cairo != NULL;
206 gboolean
207 sheet_object_can_resize (SheetObject const *so)
209 g_return_val_if_fail (IS_SHEET_OBJECT (so), FALSE);
210 return so->flags & SHEET_OBJECT_CAN_RESIZE;
213 gboolean
214 sheet_object_can_edit (SheetObject const *so)
216 g_return_val_if_fail (IS_SHEET_OBJECT (so), FALSE);
217 return so->flags & SHEET_OBJECT_CAN_EDIT;
220 static gboolean
221 sheet_object_can_prop (SheetObject const *so)
223 g_return_val_if_fail (IS_SHEET_OBJECT (so), FALSE);
224 return (sheet_object_can_edit (so) && (SO_CLASS(so)->user_config != NULL));
227 static void
228 sheet_object_populate_menu_real (SheetObject *so, GPtrArray *actions)
230 unsigned i;
231 if (so->sheet->sheet_type == GNM_SHEET_OBJECT) {
232 static SheetObjectAction const so_actions [] = {
233 { "gtk-properties", NULL, NULL, 0, sheet_object_get_editor, sheet_object_can_prop},
234 { NULL, NULL, NULL, 0, NULL, NULL },
235 { "gtk-copy", NULL, NULL, 0, cb_so_copy, NULL },
237 for (i = 0 ; i < G_N_ELEMENTS (so_actions); i++)
238 g_ptr_array_add (actions, (gpointer) (so_actions + i));
239 } else {
240 static SheetObjectAction const so_actions [] = {
241 { GTK_STOCK_PROPERTIES, NULL, NULL, 0, sheet_object_get_editor, sheet_object_can_prop},
242 { NULL, NULL, NULL, 0, NULL, NULL },
243 { GTK_STOCK_LEAVE_FULLSCREEN, N_("Size _& Position"), NULL, 0, cb_so_size_position, NULL },
244 { GTK_STOCK_FULLSCREEN, N_("_Snap to Grid"), NULL, 0, cb_so_snap_to_grid, NULL },
245 { NULL, N_("_Order"), NULL, 1, NULL, NULL },
246 { NULL, N_("Pul_l to Front"), NULL, 0, cb_so_pull_to_front, NULL },
247 { NULL, N_("Pull _Forward"), NULL, 0, cb_so_pull_forward, NULL },
248 { NULL, N_("Push _Backward"), NULL, 0, cb_so_push_backward, NULL },
249 { NULL, N_("Pus_h to Back"), NULL, 0, cb_so_push_to_back, NULL },
250 { NULL, NULL, NULL, -1, NULL, NULL },
251 { NULL, NULL, NULL, 0, NULL, NULL },
252 { GTK_STOCK_CUT, NULL, NULL, 0, cb_so_cut, NULL },
253 { GTK_STOCK_COPY, NULL, NULL, 0, cb_so_copy, NULL },
254 { GTK_STOCK_DELETE, NULL, NULL, 0, cb_so_delete, NULL },
255 { NULL, NULL, NULL, 0, NULL, NULL },
256 { GTK_STOCK_PRINT, NULL, NULL, 0, cb_so_print, sheet_object_can_print},
258 for (i = 0; i < G_N_ELEMENTS (so_actions); i++)
259 g_ptr_array_add (actions, (gpointer) (so_actions + i));
264 * sheet_object_populate_menu:
265 * @so: #SheetObject
266 * @actions: (inout) (transfer full) (element-type SheetObjectAction): #GPtrArray
268 * Get a list of the actions that can be performed on @so
270 void
271 sheet_object_populate_menu (SheetObject *so, GPtrArray *actions)
273 g_return_if_fail (NULL != so);
275 SHEET_OBJECT_CLASS (G_OBJECT_GET_CLASS(so))->populate_menu (so, actions);
279 * sheet_objects_max_extent:
280 * @sheet:
282 * Utility routine to calculate the maximum extent of objects in this sheet.
284 static void
285 sheet_objects_max_extent (Sheet *sheet)
287 GnmCellPos max_pos = { 0, 0 };
288 GSList *ptr;
290 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = ptr->next ) {
291 SheetObject *so = SHEET_OBJECT (ptr->data);
293 if (max_pos.col < so->anchor.cell_bound.end.col)
294 max_pos.col = so->anchor.cell_bound.end.col;
295 if (max_pos.row < so->anchor.cell_bound.end.row)
296 max_pos.row = so->anchor.cell_bound.end.row;
299 if (sheet->max_object_extent.col != max_pos.col ||
300 sheet->max_object_extent.row != max_pos.row) {
301 sheet->max_object_extent = max_pos;
302 sheet_scrollbar_config (sheet);
306 void
307 sheet_object_set_name (SheetObject *so, const char *name)
309 if (name == so->name)
310 return;
312 g_free (so->name);
313 so->name = g_strdup (name);
315 g_object_notify (G_OBJECT (so), "name");
318 static void
319 sheet_object_get_property (GObject *obj, guint param_id,
320 GValue *value, GParamSpec *pspec)
322 SheetObject *so = SHEET_OBJECT (obj);
324 switch (param_id) {
325 case SO_PROP_NAME:
326 g_value_set_string (value, so->name);
327 break;
328 default :
329 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
330 break;
334 static void
335 sheet_object_set_property (GObject *obj, guint param_id,
336 GValue const *value, GParamSpec *pspec)
338 SheetObject *so = SHEET_OBJECT (obj);
340 switch (param_id) {
341 case SO_PROP_NAME:
342 sheet_object_set_name (so, g_value_get_string (value));
343 break;
344 default:
345 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, param_id, pspec);
346 return;
351 static void
352 sheet_object_finalize (GObject *object)
354 SheetObject *so = SHEET_OBJECT (object);
355 if (so->sheet != NULL)
356 sheet_object_clear_sheet (so);
357 g_free (so->name);
358 parent_klass->finalize (object);
361 static void
362 sheet_object_init (GObject *object)
364 int i;
365 SheetObject *so = SHEET_OBJECT (object);
367 so->sheet = NULL;
368 so->flags = SHEET_OBJECT_IS_VISIBLE | SHEET_OBJECT_PRINT |
369 SHEET_OBJECT_CAN_RESIZE | SHEET_OBJECT_CAN_EDIT |
370 SHEET_OBJECT_MOVE_WITH_CELLS | SHEET_OBJECT_SIZE_WITH_CELLS;
372 /* Store the logical position as A1 */
373 so->anchor.cell_bound.start.col = so->anchor.cell_bound.start.row = 0;
374 so->anchor.cell_bound.end.col = so->anchor.cell_bound.end.row = 1;
375 so->anchor.base.direction = GOD_ANCHOR_DIR_UNKNOWN;
377 for (i = 4; i-- > 0 ;)
378 so->anchor.offset [i] = 0.;
381 static void
382 so_default_size (G_GNUC_UNUSED SheetObject const *so, double *width, double *height)
384 /* Provide some defaults (derived classes may want to override) */
385 *width = 72.;
386 *height = 72.;
389 static void
390 sheet_object_class_init (GObjectClass *klass)
392 SheetObjectClass *sheet_object_class = SHEET_OBJECT_CLASS (klass);
394 parent_klass = g_type_class_peek_parent (klass);
396 klass->finalize = sheet_object_finalize;
397 klass->get_property = sheet_object_get_property;
398 klass->set_property = sheet_object_set_property;
400 sheet_object_class->populate_menu = sheet_object_populate_menu_real;
401 sheet_object_class->user_config = NULL;
402 sheet_object_class->rubber_band_directly = FALSE;
403 sheet_object_class->interactive = FALSE;
404 sheet_object_class->default_size = so_default_size;
405 sheet_object_class->xml_export_name = NULL;
406 sheet_object_class->foreach_dep = NULL;
408 g_object_class_install_property
409 (klass, SO_PROP_NAME,
410 g_param_spec_string ("name", NULL, NULL, NULL,
411 GSF_PARAM_STATIC | G_PARAM_READWRITE));
413 signals [BOUNDS_CHANGED] = g_signal_new ("bounds-changed",
414 SHEET_OBJECT_TYPE,
415 G_SIGNAL_RUN_LAST,
416 G_STRUCT_OFFSET (SheetObjectClass, bounds_changed),
417 NULL, NULL,
418 g_cclosure_marshal_VOID__VOID,
419 G_TYPE_NONE, 0);
420 signals [UNREALIZED] = g_signal_new ("unrealized",
421 SHEET_OBJECT_TYPE,
422 G_SIGNAL_RUN_LAST,
423 G_STRUCT_OFFSET (SheetObjectClass, unrealized),
424 NULL, NULL,
425 g_cclosure_marshal_VOID__VOID,
426 G_TYPE_NONE, 0);
429 GSF_CLASS (SheetObject, sheet_object,
430 sheet_object_class_init, sheet_object_init,
431 G_TYPE_OBJECT)
434 * sheet_object_get_view:
435 * @so: #SheetObject
436 * @container: #SheetObjectViewContainer
438 * Returns: (transfer none): the found #SheetObjectView or %NULL.
440 SheetObjectView *
441 sheet_object_get_view (SheetObject const *so, SheetObjectViewContainer *container)
443 GList *l;
445 g_return_val_if_fail (IS_SHEET_OBJECT (so), NULL);
447 for (l = so->realized_list; l; l = l->next) {
448 SheetObjectView *view = SHEET_OBJECT_VIEW (l->data);
449 if (container == g_object_get_qdata (G_OBJECT (view), sov_container_quark))
450 return view;
453 return NULL;
457 * sheet_object_update_bounds:
458 * @so: The sheet object
459 * @p: An optional position marking the top left of the region
460 * needing relocation (default == A1)
462 * update the bounds of an object that intersects the region whose top left
463 * is @p. This is used when an objects position is anchored to cols/rows
464 * and they change position.
466 void
467 sheet_object_update_bounds (SheetObject *so, GnmCellPos const *pos)
469 gboolean is_hidden = TRUE;
470 int i, end;
472 g_return_if_fail (IS_SHEET_OBJECT (so));
474 if (pos != NULL &&
475 so->anchor.cell_bound.end.col < pos->col &&
476 so->anchor.cell_bound.end.row < pos->row)
477 return;
479 /* Are all cols hidden ? */
480 end = so->anchor.cell_bound.end.col;
481 i = so->anchor.cell_bound.start.col;
482 while (i <= end && is_hidden)
483 is_hidden &= sheet_col_is_hidden (so->sheet, i++);
485 /* Are all rows hidden ? */
486 if (!is_hidden) {
487 is_hidden = TRUE;
488 end = so->anchor.cell_bound.end.row;
489 i = so->anchor.cell_bound.start.row;
490 while (i <= end && is_hidden)
491 is_hidden &= sheet_row_is_hidden (so->sheet, i++);
492 so->flags |= SHEET_OBJECT_IS_VISIBLE;
493 } else
494 so->flags &= ~SHEET_OBJECT_IS_VISIBLE;
496 g_signal_emit (so, signals [BOUNDS_CHANGED], 0);
500 * sheet_object_get_sheet:
501 * @so: #SheetObject
503 * A small utility to help keep the implementation of SheetObjects modular.
504 * Returns: (transfer none): the #Sheet owning the object.
506 Sheet *
507 sheet_object_get_sheet (SheetObject const *so)
509 g_return_val_if_fail (IS_SHEET_OBJECT (so), NULL);
511 return so->sheet;
514 static int
515 cb_create_views (SheetObject *so)
517 g_object_set_data (G_OBJECT (so), "create_view_handler", NULL);
518 SHEET_FOREACH_CONTROL (so->sheet, view, control,
519 sc_object_create_view (control, so););
520 return FALSE;
524 * sheet_object_set_sheet:
525 * @so:
526 * @sheet:
528 * Adds a reference to the object.
530 * Returns TRUE if there was a problem
532 gboolean
533 sheet_object_set_sheet (SheetObject *so, Sheet *sheet)
535 g_return_val_if_fail (IS_SHEET_OBJECT (so), TRUE);
536 g_return_val_if_fail (IS_SHEET (sheet), TRUE);
538 if (sheet == so->sheet)
539 return FALSE;
541 g_return_val_if_fail (so->sheet == NULL, TRUE);
542 g_return_val_if_fail (g_slist_find (sheet->sheet_objects, so) == NULL, TRUE);
544 so->sheet = sheet;
545 if (SO_CLASS (so)->assign_to_sheet &&
546 SO_CLASS (so)->assign_to_sheet (so, sheet)) {
547 so->sheet = NULL;
548 return TRUE;
551 g_object_ref (so);
552 sheet->sheet_objects = g_slist_prepend (sheet->sheet_objects, so);
553 /* FIXME : add a flag to sheet to have sheet_update do this */
554 sheet_objects_max_extent (sheet);
556 if (NULL == g_object_get_data (G_OBJECT (so), "create_view_handler")) {
557 guint id = g_idle_add ((GSourceFunc) cb_create_views, so);
558 g_object_set_data (G_OBJECT (so), "create_view_handler", GUINT_TO_POINTER (id));
561 return FALSE;
565 * sheet_object_clear_sheet:
566 * @so: #SheetObject
568 * Removes @so from its container, unrealizes all views, disconects the
569 * associated data and unrefs the object
571 void
572 sheet_object_clear_sheet (SheetObject *so)
574 GSList *ptr;
575 gpointer view_handler;
577 g_return_if_fail (IS_SHEET_OBJECT (so));
579 if (so->sheet == NULL) /* already removed */
580 return;
582 g_return_if_fail (IS_SHEET (so->sheet));
584 ptr = g_slist_find (so->sheet->sheet_objects, so);
585 g_return_if_fail (ptr != NULL);
587 /* clear any pending attempts to create views */
588 view_handler = g_object_get_data (G_OBJECT (so), "create_view_handler");
589 if (NULL != view_handler) {
590 g_source_remove (GPOINTER_TO_UINT (view_handler));
591 g_object_set_data (G_OBJECT (so), "create_view_handler", NULL);
594 while (so->realized_list != NULL) {
595 g_object_set_qdata (G_OBJECT (so->realized_list->data), sov_so_quark, NULL);
596 g_object_unref (so->realized_list->data);
597 so->realized_list = g_list_remove (so->realized_list, so->realized_list->data);
600 g_signal_emit (so, signals [UNREALIZED], 0);
602 if (SO_CLASS (so)->remove_from_sheet &&
603 SO_CLASS (so)->remove_from_sheet (so))
604 return;
606 so->sheet->sheet_objects = g_slist_remove_link (so->sheet->sheet_objects, ptr);
607 g_slist_free (ptr);
609 if (so->anchor.cell_bound.end.col == so->sheet->max_object_extent.col &&
610 so->anchor.cell_bound.end.row == so->sheet->max_object_extent.row)
611 sheet_objects_max_extent (so->sheet);
613 so->sheet = NULL;
614 g_object_unref (so);
617 static void
618 cb_sheet_object_invalidate_sheet (GnmDependent *dep, G_GNUC_UNUSED SheetObject *so, gpointer user)
620 Sheet *sheet = user;
621 GnmExprRelocateInfo rinfo;
622 GnmExprTop const *texpr;
623 gboolean save_invalidated = sheet->being_invalidated;
624 gboolean dep_sheet_invalidated = (dep->sheet == sheet);
626 if (!dep->texpr)
627 return;
629 sheet->being_invalidated = TRUE;
630 rinfo.reloc_type = GNM_EXPR_RELOCATE_INVALIDATE_SHEET;
631 texpr = gnm_expr_top_relocate (dep->texpr, &rinfo, FALSE);
632 if (!texpr && dep_sheet_invalidated) {
633 texpr = dep->texpr;
634 gnm_expr_top_ref (texpr);
637 sheet->being_invalidated = save_invalidated;
639 if (texpr) {
640 gboolean was_linked = dependent_is_linked (dep);
641 dependent_set_expr (dep, texpr);
642 gnm_expr_top_unref (texpr);
643 if (dep_sheet_invalidated)
644 dep->sheet = NULL;
645 else if (was_linked)
646 dependent_link (dep);
650 void
651 sheet_object_invalidate_sheet (SheetObject *so, Sheet const *sheet)
653 sheet_object_foreach_dep (so, cb_sheet_object_invalidate_sheet,
654 (gpointer)sheet);
658 * sheet_object_foreach_dep:
659 * @so: #SheetObject
660 * @func: (scope call): #SheetObjectForeachDepFunc
661 * @user: user data
663 * Loops over each dependent contained in a sheet object and call the handler.
665 void
666 sheet_object_foreach_dep (SheetObject *so,
667 SheetObjectForeachDepFunc func,
668 gpointer user)
670 if (SO_CLASS (so)->foreach_dep)
671 SO_CLASS (so)->foreach_dep (so, func, user);
675 * sheet_object_new_view:
676 * @so:
677 * @container:
679 * Asks @so to create a view for @container.
680 * Returns: (transfer none): the new #SheetObjectView.
682 SheetObjectView *
683 sheet_object_new_view (SheetObject *so, SheetObjectViewContainer *container)
685 SheetObjectView *view;
687 g_return_val_if_fail (IS_SHEET_OBJECT (so), NULL);
688 g_return_val_if_fail (NULL != container, NULL);
690 view = sheet_object_get_view (so, container);
691 if (view != NULL)
692 return NULL;
694 view = SO_CLASS (so)->new_view (so, container);
696 if (NULL == view)
697 return NULL;
699 g_return_val_if_fail (IS_SHEET_OBJECT_VIEW (view), NULL);
701 /* Store some useful information */
702 g_object_set_qdata (G_OBJECT (view), sov_so_quark, so);
703 g_object_set_qdata (G_OBJECT (view), sov_container_quark, container);
704 so->realized_list = g_list_prepend (so->realized_list, view);
705 sheet_object_update_bounds (so, NULL);
707 return view;
711 * sheet_object_draw_cairo:
713 * Draw a sheet object using cairo.
716 * We are assuming that the cairo context is set up so that the top
717 * left of the bounds is (0,0). Note that this
718 * is the real top left cell, not necessarily the cell with to which we are
719 * anchored.
722 void
723 sheet_object_draw_cairo (SheetObject const *so, cairo_t *cr, gboolean rtl)
725 if (SO_CLASS (so)->draw_cairo) {
726 SheetObjectAnchor const *anchor;
727 double x = 0., y = 0., width, height, cell_width, cell_height;
728 anchor = sheet_object_get_anchor (so);
729 cell_width = sheet_col_get_distance_pts (so->sheet,
730 anchor->cell_bound.start.col,
731 anchor->cell_bound.start.col + 1);
732 cell_height = sheet_row_get_distance_pts (so->sheet,
733 anchor->cell_bound.start.row,
734 anchor->cell_bound.start.row + 1);
735 x = cell_width * anchor->offset[0];
737 y = cell_height * anchor->offset[1];
738 cell_width = sheet_col_get_distance_pts (so->sheet,
739 anchor->cell_bound.end.col,
740 anchor->cell_bound.end.col + 1);
741 cell_height = sheet_row_get_distance_pts (so->sheet,
742 anchor->cell_bound.end.row,
743 anchor->cell_bound.end.row + 1);
745 if (rtl) {
746 x = cell_width * (1 - anchor->offset[2]);
748 if (sheet_object_can_resize (so)) {
749 width = sheet_col_get_distance_pts (so->sheet,
750 anchor->cell_bound.start.col,
751 anchor->cell_bound.end.col + 1);
752 height = sheet_row_get_distance_pts (so->sheet,
753 anchor->cell_bound.start.row,
754 anchor->cell_bound.end.row + 1);
755 width -= x;
756 height -= y;
757 width -= cell_width * (1. - anchor->offset[2]);
758 height -= cell_height * (1 - anchor->offset[3]);
759 } else
760 sheet_object_default_size ((SheetObject *) so, &width, &height);
762 /* we don't need to save/restore cairo, the caller must do it */
763 cairo_translate (cr, x, y);
764 SO_CLASS (so)->draw_cairo (so, cr, width, height);
768 void
769 sheet_object_draw_cairo_sized (SheetObject const *so, cairo_t *cr, double width, double height)
771 SO_CLASS (so)->draw_cairo (so, cr, width, height);
774 GnmRange const *
775 sheet_object_get_range (SheetObject const *so)
777 g_return_val_if_fail (IS_SHEET_OBJECT (so), NULL);
779 return &so->anchor.cell_bound;
783 * sheet_object_get_anchor:
784 * @so: #SheetObject
786 * Returns: (transfer none): the #SheetObjectAnchor for @so.
788 SheetObjectAnchor const *
789 sheet_object_get_anchor (SheetObject const *so)
791 g_return_val_if_fail (IS_SHEET_OBJECT (so), NULL);
793 return &so->anchor;
796 void
797 sheet_object_set_anchor (SheetObject *so, SheetObjectAnchor const *anchor)
799 g_return_if_fail (IS_SHEET_OBJECT (so));
801 so->anchor = *anchor;
802 if (so->sheet != NULL) {
803 sheet_objects_max_extent (so->sheet);
804 sheet_object_update_bounds (so, NULL);
808 SheetObjectAnchor *
809 sheet_object_anchor_dup (SheetObjectAnchor const *src)
811 SheetObjectAnchor *res = g_memdup (src, sizeof (SheetObjectAnchor));
812 return res;
815 static double
816 cell_offset_calc_pt (Sheet const *sheet, int i, gboolean is_col, double offset)
818 ColRowInfo const *cri = sheet_colrow_get_info (sheet, i, is_col);
819 return offset * cri->size_pts;
823 * sheet_object_default_size:
824 * @so: The sheet object
825 * @w: a ptr into which to store the default_width.
826 * @h: a ptr into which to store the default_height.
828 * Measurements are in pts.
830 void
831 sheet_object_default_size (SheetObject *so, double *w, double *h)
833 g_return_if_fail (IS_SHEET_OBJECT (so));
834 g_return_if_fail (w != NULL);
835 g_return_if_fail (h != NULL);
837 SO_CLASS (so)->default_size (so, w, h);
841 * sheet_object_position_pts_get:
842 * @so: The sheet object
843 * @coords: array of 4 doubles
845 * Calculate the position of the object @so in pts from the logical position in
846 * the object.
848 void
849 sheet_object_position_pts_get (SheetObject const *so, double *coords)
851 g_return_if_fail (IS_SHEET_OBJECT (so));
852 sheet_object_anchor_to_pts (&so->anchor, so->sheet, coords);
855 void
856 sheet_object_anchor_to_pts (SheetObjectAnchor const *anchor,
857 Sheet const *sheet, double *res_pts)
859 GnmRange const *r;
861 g_return_if_fail (res_pts != NULL);
863 r = &anchor->cell_bound;
865 res_pts [0] = sheet_col_get_distance_pts (sheet, 0,
866 r->start.col);
867 res_pts [2] = res_pts [0] + sheet_col_get_distance_pts (sheet,
868 r->start.col, r->end.col);
869 res_pts [1] = sheet_row_get_distance_pts (sheet, 0,
870 r->start.row);
871 res_pts [3] = res_pts [1] + sheet_row_get_distance_pts (sheet,
872 r->start.row, r->end.row);
874 res_pts [0] += cell_offset_calc_pt (sheet, r->start.col,
875 TRUE, anchor->offset [0]);
876 res_pts [1] += cell_offset_calc_pt (sheet, r->start.row,
877 FALSE, anchor->offset [1]);
878 res_pts [2] += cell_offset_calc_pt (sheet, r->end.col,
879 TRUE, anchor->offset [2]);
880 res_pts [3] += cell_offset_calc_pt (sheet, r->end.row,
881 FALSE, anchor->offset [3]);
884 void
885 sheet_object_anchor_to_offset_pts (SheetObjectAnchor const *anchor,
886 Sheet const *sheet, double *res_pts)
888 GnmRange const *r;
890 g_return_if_fail (res_pts != NULL);
892 r = &anchor->cell_bound;
894 res_pts [0] = cell_offset_calc_pt (sheet, r->start.col,
895 TRUE, anchor->offset [0]);
896 res_pts [1] = cell_offset_calc_pt (sheet, r->start.row,
897 FALSE, anchor->offset [1]);
898 res_pts [2] = cell_offset_calc_pt (sheet, r->end.col,
899 TRUE, anchor->offset [2]);
900 res_pts [3] = cell_offset_calc_pt (sheet, r->end.row,
901 FALSE, anchor->offset [3]);
904 static void
905 clear_sheet (SheetObject *so, GOUndo **pundo)
907 if (pundo) {
908 GOUndo *u = go_undo_binary_new
909 (g_object_ref (so),
910 so->sheet,
911 (GOUndoBinaryFunc)sheet_object_set_sheet,
912 (GFreeFunc) g_object_unref,
913 NULL);
914 *pundo = go_undo_combine (*pundo, u);
917 sheet_object_clear_sheet (so);
922 * sheet_objects_relocate:
923 * @rinfo: details on what should be moved.
924 * @update: Should we do the bound_update now, or leave it for later.
925 * if FALSE honour the move_with_cells flag.
926 * @pundo: if non-NULL add dropped objects to ::objects
928 * Uses the relocation info and the anchors to decide whether or not, and how
929 * to relocate objects when the grid moves (eg ins/del col/row).
931 void
932 sheet_objects_relocate (GnmExprRelocateInfo const *rinfo, gboolean update,
933 GOUndo **pundo)
935 GSList *ptr, *next;
936 GnmRange dest;
937 gboolean change_sheets;
939 g_return_if_fail (rinfo != NULL);
940 g_return_if_fail (IS_SHEET (rinfo->origin_sheet));
941 g_return_if_fail (IS_SHEET (rinfo->target_sheet));
943 dest = rinfo->origin;
944 range_translate (&dest, rinfo->target_sheet,
945 rinfo->col_offset, rinfo->row_offset);
946 change_sheets = (rinfo->origin_sheet != rinfo->target_sheet);
948 /* Clear the destination range on the target sheet */
949 if (change_sheets) {
950 GSList *copy = g_slist_copy (rinfo->target_sheet->sheet_objects);
951 for (ptr = copy; ptr != NULL ; ptr = ptr->next ) {
952 SheetObject *so = SHEET_OBJECT (ptr->data);
953 GnmRange const *r = &so->anchor.cell_bound;
954 if (range_contains (&dest, r->start.col, r->start.row)) {
955 clear_sheet (so, pundo);
958 g_slist_free (copy);
961 ptr = rinfo->origin_sheet->sheet_objects;
962 for (; ptr != NULL ; ptr = next ) {
963 SheetObject *so = SHEET_OBJECT (ptr->data);
964 GnmRange r = so->anchor.cell_bound;
966 next = ptr->next;
967 if (update && 0 == (so->flags & SHEET_OBJECT_MOVE_WITH_CELLS))
968 continue;
969 if (range_contains (&rinfo->origin, r.start.col, r.start.row)) {
970 /* FIXME : just moving the range is insufficent for all anchor types */
971 /* Toss any objects that would be clipped. */
972 if (range_translate (&r, rinfo->origin_sheet,
973 rinfo->col_offset, rinfo->row_offset)) {
974 clear_sheet (so, pundo);
975 continue;
977 so->anchor.cell_bound = r;
979 if (change_sheets) {
980 g_object_ref (so);
981 sheet_object_clear_sheet (so);
982 sheet_object_set_sheet (so, rinfo->target_sheet);
983 g_object_unref (so);
984 } else if (update)
985 sheet_object_update_bounds (so, NULL);
986 } else if (!change_sheets &&
987 range_contains (&dest, r.start.col, r.start.row)) {
988 clear_sheet (so, pundo);
989 continue;
993 sheet_objects_max_extent (rinfo->origin_sheet);
994 if (change_sheets)
995 sheet_objects_max_extent (rinfo->target_sheet);
999 * sheet_objects_get:
1000 * @sheet: the sheet.
1001 * @r: an optional range to look in
1002 * @t: The type of object to lookup
1004 * Returns: (element-type SheetObject) (transfer container): a list of which
1005 * the caller must free (just the list not the content).
1006 * Containing all objects of exactly the specified type (inheritence does not count)
1007 * that are completely contained by @r.
1009 GSList *
1010 sheet_objects_get (Sheet const *sheet, GnmRange const *r, GType t)
1012 GSList *res = NULL;
1013 GSList *ptr;
1015 g_return_val_if_fail (IS_SHEET (sheet), NULL);
1017 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = ptr->next ) {
1018 GObject *obj = G_OBJECT (ptr->data);
1020 if (t == G_TYPE_NONE || t == G_OBJECT_TYPE (obj)) {
1021 SheetObject *so = SHEET_OBJECT (obj);
1022 if (r == NULL || range_contained (&so->anchor.cell_bound, r))
1023 res = g_slist_prepend (res, so);
1026 return g_slist_reverse (res);
1030 * sheet_objects_clear:
1031 * @sheet: the sheet.
1032 * @r: an optional range to look in
1034 * removes the objects in the region.
1036 void
1037 sheet_objects_clear (Sheet const *sheet, GnmRange const *r, GType t,
1038 GOUndo **pundo)
1040 GSList *ptr, *next;
1042 g_return_if_fail (IS_SHEET (sheet));
1044 for (ptr = sheet->sheet_objects; ptr != NULL ; ptr = next ) {
1045 GObject *obj = G_OBJECT (ptr->data);
1046 next = ptr->next;
1047 if ((t == G_TYPE_NONE && G_OBJECT_TYPE (obj) != GNM_FILTER_COMBO_TYPE)
1048 || t == G_OBJECT_TYPE (obj)) {
1049 SheetObject *so = SHEET_OBJECT (obj);
1050 if (r == NULL || range_contained (&so->anchor.cell_bound, r))
1051 clear_sheet (so, pundo);
1057 * sheet_object_dup:
1058 * @so: a #SheetObject to duplicate
1060 * Returns: (transfer full): A copy of @so that is not attached to a sheet.
1061 * Caller is responsible for the reference.
1063 SheetObject *
1064 sheet_object_dup (SheetObject const *so)
1066 SheetObject *new_so = NULL;
1068 if (!SO_CLASS (so)->copy)
1069 return NULL;
1071 new_so = g_object_new (G_OBJECT_TYPE (so), NULL);
1073 g_return_val_if_fail (new_so != NULL, NULL);
1075 SO_CLASS (so)->copy (new_so, so);
1076 new_so->flags = so->flags;
1077 new_so->anchor = so->anchor;
1079 return new_so;
1082 static void
1083 cb_sheet_objects_dup (GnmDependent *dep, SheetObject *so, gpointer user)
1085 Sheet *src = user;
1086 Sheet *dst = sheet_object_get_sheet (so);
1087 GnmExprTop const *texpr;
1089 if (!dep->texpr)
1090 return;
1092 texpr = gnm_expr_top_relocate_sheet (dep->texpr, src, dst);
1093 if (texpr != dep->texpr) {
1094 gboolean was_linked= dependent_is_linked (dep);
1095 dependent_set_expr (dep, texpr);
1096 if (was_linked)
1097 dependent_link (dep);
1099 gnm_expr_top_unref (texpr);
1104 * sheet_objects_dup:
1105 * @src: The source sheet to read the objects from
1106 * @dst: The destination sheet to attach the objects to
1107 * @range: Optionally NULL region of interest
1109 * Clones the objects of the src sheet and attaches them into the dst sheet
1111 void
1112 sheet_objects_dup (Sheet const *src, Sheet *dst, GnmRange *range)
1114 GSList *list;
1116 g_return_if_fail (IS_SHEET (dst));
1117 g_return_if_fail (dst->sheet_objects == NULL);
1119 for (list = src->sheet_objects; list != NULL; list = list->next) {
1120 SheetObject *so = list->data;
1121 if (range == NULL || range_overlap (range, &so->anchor.cell_bound)) {
1122 SheetObject *new_so = sheet_object_dup (so);
1123 if (new_so != NULL) {
1124 sheet_object_set_sheet (new_so, dst);
1125 sheet_object_foreach_dep (new_so, cb_sheet_objects_dup,
1126 (gpointer)src);
1127 g_object_unref (new_so);
1132 dst->sheet_objects = g_slist_reverse (dst->sheet_objects);
1137 * sheet_object_direction_set:
1138 * @so: The sheet object that we are calculating the direction for
1139 * @coords: array of coordinates in L,T,R,B order
1141 * Sets the object direction from the given the new coordinates
1142 * The original coordinates are assumed to be normalized (so that top
1143 * is above bottom and right is at the right of left)
1145 void
1146 sheet_object_direction_set (SheetObject *so, gdouble const *coords)
1148 if (so->anchor.base.direction == GOD_ANCHOR_DIR_UNKNOWN)
1149 return;
1151 so->anchor.base.direction = GOD_ANCHOR_DIR_NONE_MASK;
1153 if (coords [1] < coords [3])
1154 so->anchor.base.direction |= GOD_ANCHOR_DIR_DOWN;
1155 if (coords [0] < coords [2])
1156 so->anchor.base.direction |= GOD_ANCHOR_DIR_RIGHT;
1160 * sheet_object_rubber_band_directly:
1161 * @so:
1163 * Returns TRUE if we should draw the object as we are laying it out on
1164 * an sheet. If FLASE we draw a rectangle where the object is going to go
1166 * Return Value:
1168 gboolean
1169 sheet_object_rubber_band_directly (G_GNUC_UNUSED SheetObject const *so)
1171 return FALSE;
1175 * sheet_object_anchor_init:
1176 * @anchor: #SheetObjectAnchor
1177 * @cell_bound: #GnmRange
1178 * @offsets: double[4]
1179 * @direction: #GODrawingAnchorDir
1181 * A utility routine to initialize an anchor. Useful in case we change fields
1182 * in the future and want to ensure that everything is initialized.
1184 void
1185 sheet_object_anchor_init (SheetObjectAnchor *anchor,
1186 GnmRange const *r, const double *offsets,
1187 GODrawingAnchorDir direction)
1189 int i;
1191 if (r == NULL) {
1192 static GnmRange const defaultVal = { { 0, 0 }, { 1, 1 } };
1193 r = &defaultVal;
1195 anchor->cell_bound = *r;
1197 if (offsets == NULL) {
1198 static double const defaultVal [4] = { 0., 0., 0., 0. };
1199 offsets = defaultVal;
1201 for (i = 4; i-- > 0 ; )
1202 anchor->offset[i] = offsets [i];
1204 anchor->base.direction = direction;
1205 /* TODO : add sanity checking to handle offsets past edges of col/row */
1208 /*****************************************************************************/
1211 * sheet_object_get_stacking:
1212 * @so: #SheetObject
1214 * Returns @so's position in the stack of sheet objects.
1216 gint
1217 sheet_object_get_stacking (SheetObject *so)
1219 GSList *ptr;
1220 int pos = 0;
1222 g_return_val_if_fail (so != NULL, 0);
1223 g_return_val_if_fail (so->sheet != NULL, 0);
1225 for (ptr = so->sheet->sheet_objects ; ptr ; ptr = ptr->next, pos++)
1226 if (ptr->data == so)
1227 return pos;
1229 g_warning ("Object not found??");
1230 return 0;
1233 gint
1234 sheet_object_adjust_stacking (SheetObject *so, gint offset)
1236 GList *l;
1237 GSList **ptr, *node = NULL;
1238 int i, target, cur = 0;
1240 g_return_val_if_fail (so != NULL, 0);
1241 g_return_val_if_fail (so->sheet != NULL, 0);
1243 for (ptr = &so->sheet->sheet_objects ; *ptr ; ptr = &(*ptr)->next, cur++)
1244 if ((*ptr)->data == so) {
1245 node = *ptr;
1246 *ptr = (*ptr)->next;
1247 break;
1250 g_return_val_if_fail (node != NULL, 0);
1252 /* Start at the begining when moving things towards the front */
1253 if (offset > 0) {
1254 ptr = &so->sheet->sheet_objects;
1255 i = 0;
1256 } else
1257 i = cur;
1259 for (target = cur - offset; *ptr && i < target ; ptr = &(*ptr)->next)
1260 i++;
1262 node->next = *ptr;
1263 *ptr = node;
1265 /* TODO : Move this to the container */
1266 for (l = so->realized_list; l; l = l->next) {
1267 GocItem *item = GOC_ITEM (l->data);
1268 if (offset > 0)
1269 goc_item_raise (item, offset);
1270 else
1271 goc_item_lower (item, - offset);
1273 return cur - i;
1276 /*****************************************************************************/
1278 static GObjectClass *view_parent_class;
1280 void
1281 sheet_object_view_set_bounds (SheetObjectView *sov,
1282 double const *coords, gboolean visible)
1284 SheetObjectViewClass *klass;
1286 g_return_if_fail (IS_SHEET_OBJECT_VIEW (sov));
1287 klass = SHEET_OBJECT_VIEW_GET_CLASS (sov);
1288 if (NULL != klass->set_bounds)
1289 (klass->set_bounds) (sov, coords, visible);
1293 * sheet_object_view_get_so:
1294 * @sov: #SheetObjectView
1296 * Returns: (transfer none): the #SheetObject owning @view.
1298 SheetObject *
1299 sheet_object_view_get_so (SheetObjectView *view)
1301 return g_object_get_qdata (G_OBJECT (view), sov_so_quark);
1304 static gboolean
1305 sheet_object_view_enter_notify (GocItem *item, double x, double y)
1307 SheetObject *so;
1309 if (IS_GNM_PANE (item->canvas) && scg_wbcg (GNM_SIMPLE_CANVAS (item->canvas)->scg)->new_object) {
1310 GnmItemGrid *grid = GNM_PANE (item->canvas)->grid;
1311 return GOC_ITEM_GET_CLASS (grid)->enter_notify (GOC_ITEM (grid), x, y);
1314 so = (SheetObject *) g_object_get_qdata (G_OBJECT (item), sov_so_quark);
1315 gnm_widget_set_cursor_type (GTK_WIDGET (item->canvas),
1316 (so->flags & SHEET_OBJECT_CAN_PRESS) ? GDK_HAND2 : GDK_ARROW);
1317 return FALSE;
1320 static void
1321 cb_so_menu_activate (GObject *menu, GocItem *view)
1323 SheetObjectAction const *a = g_object_get_data (menu, "action");
1325 if (a->func) {
1326 SheetObject *so = sheet_object_view_get_so (SHEET_OBJECT_VIEW (view));
1327 gpointer data = g_object_get_data (G_OBJECT (view->canvas), "sheet-control");
1329 if (data == NULL)
1330 data = GNM_SIMPLE_CANVAS (view->canvas)->scg;
1332 (a->func) (so, SHEET_CONTROL (data));
1336 static void
1337 cb_ptr_array_free (GPtrArray *actions)
1339 g_ptr_array_free (actions, TRUE);
1343 * sheet_object_build_menu:
1344 * @view: #SheetObjectView
1345 * @actions: (element-type SheetObjectAction): array of actions.
1346 * @i: index of first action to add in the array.
1348 * Builds the contextual menu for @view.
1349 * Returns: (transfer full): the newly constructed #GtkMenu.
1351 GtkWidget *
1352 sheet_object_build_menu (SheetObjectView *view,
1353 GPtrArray const *actions, unsigned *i)
1355 SheetObjectAction const *a;
1356 GtkWidget *item, *menu = gtk_menu_new ();
1358 while (*i < actions->len) {
1359 a = g_ptr_array_index (actions, *i);
1360 (*i)++;
1361 if (a->submenu < 0)
1362 break;
1363 if (a->icon != NULL) {
1364 if (a->label != NULL) {
1365 item = gtk_image_menu_item_new_with_mnemonic (_(a->label));
1366 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
1367 gtk_image_new_from_stock (a->icon, GTK_ICON_SIZE_MENU));
1368 } else
1369 item = gtk_image_menu_item_new_from_stock (a->icon, NULL);
1370 } else if (a->label != NULL)
1371 item = gtk_menu_item_new_with_mnemonic (_(a->label));
1372 else
1373 item = gtk_separator_menu_item_new ();
1374 if (a->submenu > 0)
1375 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item),
1376 sheet_object_build_menu (view, actions, i));
1377 else if (a->label != NULL || a->icon != NULL) { /* not a separator or menu */
1378 g_object_set_data (G_OBJECT (item), "action", (gpointer)a);
1379 g_signal_connect_object (G_OBJECT (item), "activate",
1380 G_CALLBACK (cb_so_menu_activate), view, 0);
1381 gtk_widget_set_sensitive (item, a->enable_func == NULL
1382 || a->enable_func (sheet_object_view_get_so (view)));
1384 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1386 return menu;
1389 static gboolean
1390 sheet_object_view_button_pressed (GocItem *item, int button, double x, double y)
1392 GnmPane *pane;
1393 SheetObject *so;
1394 if (IS_GNM_PANE (item->canvas)) {
1395 if (scg_wbcg (GNM_SIMPLE_CANVAS (item->canvas)->scg)->new_object) {
1396 GnmItemGrid *grid = GNM_PANE (item->canvas)->grid;
1397 return GOC_ITEM_GET_CLASS (grid)->button_pressed (GOC_ITEM (grid), button, x, y);
1400 if (button > 3)
1401 return FALSE;
1403 pane = GNM_PANE (item->canvas);
1404 so = (SheetObject *) g_object_get_qdata (G_OBJECT (item), sov_so_quark);
1406 x *= goc_canvas_get_pixels_per_unit (item->canvas);
1407 y *= goc_canvas_get_pixels_per_unit (item->canvas);
1408 /* cb_sheet_object_widget_canvas_event calls even if selected */
1409 if (NULL == g_hash_table_lookup (pane->drag.ctrl_pts, so)) {
1410 SheetObjectClass *soc =
1411 G_TYPE_INSTANCE_GET_CLASS (so, SHEET_OBJECT_TYPE, SheetObjectClass);
1412 GdkEventButton *event = (GdkEventButton *) goc_canvas_get_cur_event (item->canvas);
1414 if (soc->interactive && button != 3)
1415 return FALSE;
1417 if (!(event->state & GDK_SHIFT_MASK))
1418 scg_object_unselect (pane->simple.scg, NULL);
1419 scg_object_select (pane->simple.scg, so);
1420 if (NULL == g_hash_table_lookup (pane->drag.ctrl_pts, so))
1421 return FALSE; /* protected ? */
1424 if (button < 3)
1425 gnm_pane_object_start_resize (pane, button, x, y, so, 8, FALSE);
1426 else
1427 gnm_pane_display_object_menu (pane, so, goc_canvas_get_cur_event (item->canvas));
1428 } else {
1429 if (button == 3) {
1430 GPtrArray *actions = g_ptr_array_new ();
1431 GtkWidget *menu;
1432 unsigned i = 0;
1434 so = (SheetObject *) g_object_get_qdata (G_OBJECT (item), sov_so_quark);
1435 sheet_object_populate_menu (so, actions);
1437 if (actions->len == 0) {
1438 g_ptr_array_free (actions, TRUE);
1439 return FALSE;
1442 menu = sheet_object_build_menu
1443 (sheet_object_get_view (so, (SheetObjectViewContainer *) item->canvas),
1444 actions, &i);
1445 g_object_set_data_full (G_OBJECT (menu), "actions", actions,
1446 (GDestroyNotify) cb_ptr_array_free);
1447 gtk_widget_show_all (menu);
1448 gnumeric_popup_menu (GTK_MENU (menu),
1449 goc_canvas_get_cur_event (item->canvas));
1452 return TRUE;
1455 static gboolean
1456 sheet_object_view_button2_pressed (GocItem *item, int button, double x, double y)
1458 if (button == 1 && !IS_GNM_PANE (item->canvas)) {
1459 SheetControl *sc = SHEET_CONTROL (g_object_get_data (G_OBJECT (item->canvas), "sheet-control"));
1460 SheetObject *so = (SheetObject *) g_object_get_qdata (G_OBJECT (item), sov_so_quark);
1462 if (sc && sheet_object_can_edit (so))
1463 sheet_object_get_editor (so, sc);
1465 return TRUE;
1468 static void
1469 sheet_object_view_finalize (GObject *obj)
1471 SheetObject *so = (SheetObject *) g_object_get_qdata (obj, sov_so_quark);
1472 if (so)
1473 so->realized_list = g_list_remove (so->realized_list, obj);
1474 view_parent_class->finalize (obj);
1477 static void
1478 sheet_object_view_class_init (GocItemClass *item_klass)
1480 GObjectClass *obj_klass = (GObjectClass *) item_klass;
1481 view_parent_class = g_type_class_peek_parent (item_klass);
1483 obj_klass->finalize = sheet_object_view_finalize;
1485 item_klass->enter_notify = sheet_object_view_enter_notify;
1486 item_klass->button_pressed = sheet_object_view_button_pressed;
1487 item_klass->button2_pressed = sheet_object_view_button2_pressed;
1490 GSF_CLASS (SheetObjectView, sheet_object_view,
1491 sheet_object_view_class_init, NULL,
1492 GOC_TYPE_GROUP)
1494 /*****************************************************************************/
1496 GType
1497 sheet_object_imageable_get_type (void)
1499 static GType type = 0;
1501 if (!type) {
1502 static GTypeInfo const type_info = {
1503 sizeof (SheetObjectImageableIface), /* class_size */
1504 NULL, /* base_init */
1505 NULL, /* base_finalize */
1506 NULL, NULL, NULL, 0, 0, NULL, NULL
1509 type = g_type_register_static (G_TYPE_INTERFACE,
1510 "SheetObjectImageable", &type_info, 0);
1513 return type;
1516 #define SHEET_OBJECT_IMAGEABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), SHEET_OBJECT_IMAGEABLE_TYPE, SheetObjectImageableIface))
1518 GtkTargetList *
1519 sheet_object_get_target_list (SheetObject const *so)
1521 if (!IS_SHEET_OBJECT_IMAGEABLE (so))
1522 return NULL;
1524 return SHEET_OBJECT_IMAGEABLE_CLASS (so)->get_target_list (so);
1527 void
1528 sheet_object_write_image (SheetObject const *so, char const *format, double resolution,
1529 GsfOutput *output, GError **err)
1531 g_return_if_fail (IS_SHEET_OBJECT_IMAGEABLE (so));
1533 SHEET_OBJECT_IMAGEABLE_CLASS (so)->write_image (so, format, resolution,
1534 output, err);
1538 /*****************************************************************************/
1540 GType
1541 sheet_object_exportable_get_type (void)
1543 static GType type = 0;
1545 if (!type) {
1546 static GTypeInfo const type_info = {
1547 sizeof (SheetObjectExportableIface), /* class_size */
1548 NULL, /* base_init */
1549 NULL, /* base_finalize */
1550 NULL, NULL, NULL, 0, 0, NULL, NULL
1553 type = g_type_register_static (G_TYPE_INTERFACE,
1554 "SheetObjectExportable", &type_info, 0);
1557 return type;
1560 #define SHEET_OBJECT_EXPORTABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), SHEET_OBJECT_EXPORTABLE_TYPE, SheetObjectExportableIface))
1562 GtkTargetList *
1563 sheet_object_exportable_get_target_list (SheetObject const *so)
1565 if (!IS_SHEET_OBJECT_EXPORTABLE (so))
1566 return NULL;
1568 return SHEET_OBJECT_EXPORTABLE_CLASS (so)->get_target_list (so);
1571 void
1572 sheet_object_write_object (SheetObject const *so, char const *format,
1573 GsfOutput *output, GError **err,
1574 GnmConventions const *convs)
1576 GnmLocale *locale;
1578 g_return_if_fail (IS_SHEET_OBJECT_EXPORTABLE (so));
1580 locale = gnm_push_C_locale ();
1581 SHEET_OBJECT_EXPORTABLE_CLASS (so)->
1582 write_object (so, format, output, err, convs);
1583 gnm_pop_C_locale (locale);
1587 * sheet_object_move_undo:
1588 * @objects: (element-type SheetObject):
1589 * @objects_created:
1591 * Returns: (transfer full): the newly allocated #GOUndo.
1593 GOUndo *
1594 sheet_object_move_undo (GSList *objects, gboolean objects_created)
1596 GOUndo *undo = NULL;
1597 GSList *objs = objects;
1599 g_return_val_if_fail (NULL != objects, NULL);
1601 for (; objs; objs = objs->next) {
1602 SheetObject *obj = objs->data;
1603 SheetObjectAnchor *tmp;
1605 if (objects_created) {
1606 undo = go_undo_combine
1607 (undo,
1608 go_undo_unary_new
1609 (g_object_ref (obj),
1610 (GOUndoUnaryFunc) sheet_object_clear_sheet,
1611 (GFreeFunc) g_object_unref));
1614 tmp = g_new (SheetObjectAnchor, 1);
1615 *tmp = *sheet_object_get_anchor (obj);
1616 undo = go_undo_combine
1617 (undo, go_undo_binary_new
1618 (g_object_ref (obj), tmp,
1619 (GOUndoBinaryFunc) sheet_object_set_anchor,
1620 (GFreeFunc) g_object_unref,
1621 (GFreeFunc) g_free));
1623 return undo;
1627 * sheet_object_move_do:
1628 * @objects: (element-type SheetObject):
1629 * @anchors: (element-type SheetObjectAnchor):
1630 * @objects_created:
1632 * Returns: (transfer full): the newly allocated #GOUndo.
1634 GOUndo *
1635 sheet_object_move_do (GSList *objects, GSList *anchors,
1636 gboolean objects_created)
1638 GOUndo *undo = NULL;
1639 GSList *objs = objects, *anchs = anchors;
1641 g_return_val_if_fail (NULL != objects, NULL);
1642 g_return_val_if_fail (NULL != anchors, NULL);
1643 g_return_val_if_fail (g_slist_length (objects)
1644 == g_slist_length (anchors), NULL);
1646 for (; objs && anchs; objs = objs->next, anchs = anchs->next) {
1647 SheetObject *obj = objs->data;
1648 SheetObjectAnchor *anch = anchs->data;
1649 SheetObjectAnchor *tmp;
1651 if (objects_created) {
1652 undo = go_undo_combine
1653 (undo,
1654 go_undo_binary_new
1655 (g_object_ref (obj),
1656 sheet_object_get_sheet (obj),
1657 (GOUndoBinaryFunc) sheet_object_set_sheet,
1658 (GFreeFunc) g_object_unref,
1659 NULL));
1661 tmp = g_new (SheetObjectAnchor, 1);
1662 *tmp = *anch;
1663 undo = go_undo_combine
1664 (go_undo_binary_new
1665 (g_object_ref (obj), tmp,
1666 (GOUndoBinaryFunc) sheet_object_set_anchor,
1667 (GFreeFunc) g_object_unref,
1668 (GFreeFunc) g_free), undo);
1670 return undo;
1674 /*****************************************************************************/
1676 void
1677 sheet_objects_init (void)
1679 GNM_SO_LINE_TYPE;
1680 GNM_SO_FILLED_TYPE;
1681 SHEET_OBJECT_GRAPH_TYPE;
1682 SHEET_OBJECT_IMAGE_TYPE;
1683 GNM_GO_DATA_SCALAR_TYPE;
1684 GNM_GO_DATA_VECTOR_TYPE;
1685 GNM_GO_DATA_MATRIX_TYPE;
1686 CELL_COMMENT_TYPE;
1688 sheet_object_widget_register ();
1689 sov_so_quark = g_quark_from_static_string ("SheetObject");
1690 sov_container_quark = g_quark_from_static_string ("SheetObjectViewContainer");