2 * sheet-object.c: Implements the sheet object manipulation for Gnumeric
5 * Miguel de Icaza (miguel@kernel.org)
6 * Michael Meeks (mmeeks@gnu.org)
7 * Jody Goldberg (jody@gnome.org)
9 #include <gnumeric-config.h>
10 #include <glib/gi18n-lib.h>
12 #include <sheet-object.h>
15 #include <dependent.h>
16 #include <sheet-view.h>
17 #include <sheet-control.h>
18 #include <sheet-control-gui.h>
19 #include <sheet-private.h>
20 #include <dialogs/dialogs.h>
21 #include <sheet-object-impl.h>
27 #include <gnm-pane-impl.h>
28 #include <gnm-so-line.h>
29 #include <gnm-so-filled.h>
30 #include <sheet-control-gui-priv.h>
31 #include <sheet-object-cell-comment.h>
32 #include <sheet-object-widget.h>
33 #include <sheet-object-graph.h>
34 #include <sheet-object-image.h>
35 #include <sheet-filter-combo.h>
36 #include <wbc-gtk-impl.h>
39 #include <goffice/goffice.h>
40 #include <application.h>
43 #include <libxml/globals.h>
44 #include <gsf/gsf-impl-utils.h>
48 /* GType code for SheetObjectAnchor */
49 static SheetObjectAnchor
*
50 sheet_object_anchor_copy (SheetObjectAnchor
* soa
)
52 SheetObjectAnchor
*res
= g_new (SheetObjectAnchor
, 1);
58 sheet_object_anchor_get_type (void)
63 t
= g_boxed_type_register_static ("SheetObjectAnchor",
64 (GBoxedCopyFunc
)sheet_object_anchor_copy
,
65 (GBoxedFreeFunc
)g_free
);
71 gnm_sheet_object_anchor_mode_get_type (void)
73 static GType etype
= 0;
75 static GEnumValue
const values
[] = {
76 { GNM_SO_ANCHOR_TWO_CELLS
, "GNM_SO_ANCHOR_TWO_CELLS", "two-cells" },
77 { GNM_SO_ANCHOR_ONE_CELL
, "GNM_SO_ANCHOR_ONE_CELL", "one-cell" },
78 { GNM_SO_ANCHOR_ABSOLUTE
, "GNM_SO_ANCHOR_ABSOLUTE", "absolute" },
81 etype
= g_enum_register_static ("GnmSOAnchorMode", values
);
87 /* Returns the class for a SheetObject */
88 #define SO_CLASS(so) GNM_SO_CLASS(G_OBJECT_GET_CLASS(so))
100 static guint signals
[LAST_SIGNAL
] = { 0 };
101 static GObjectClass
*parent_klass
;
102 static GQuark sov_so_quark
;
103 static GQuark sov_container_quark
;
106 sheet_object_set_print_flag (SheetObject
*so
, gboolean
*print
)
109 so
->flags
|= SHEET_OBJECT_PRINT
;
111 so
->flags
&= ~SHEET_OBJECT_PRINT
;
116 cb_so_size_position (SheetObject
*so
, SheetControl
*sc
)
120 g_return_if_fail (GNM_IS_SCG (sc
));
122 wbcg
= scg_wbcg ((SheetControlGUI
*)sc
);
124 if (wbcg
->edit_line
.guru
!= NULL
) {
125 GtkWidget
*w
= wbcg
->edit_line
.guru
;
126 wbc_gtk_detach_guru (wbcg
);
127 gtk_widget_destroy (w
);
130 dialog_so_size (wbcg
, G_OBJECT (so
));
134 cb_so_snap_to_grid (SheetObject
*so
, SheetControl
*sc
)
136 SheetObjectAnchor
*snapped
=
137 sheet_object_anchor_dup (sheet_object_get_anchor (so
));
138 GnmSOAnchorMode mode
= snapped
->mode
;
139 snapped
->mode
= GNM_SO_ANCHOR_TWO_CELLS
;
140 snapped
->offset
[0] = snapped
->offset
[1] = 0.;
141 snapped
->offset
[2] = snapped
->offset
[3] = 1.;
142 if (mode
!= GNM_SO_ANCHOR_TWO_CELLS
) {
144 sheet_object_anchor_to_pts (snapped
, so
->sheet
, pts
);
145 snapped
->mode
= mode
;
146 sheet_object_pts_to_anchor (snapped
, so
->sheet
, pts
);
148 cmd_objects_move (sc_wbc (sc
),
149 g_slist_prepend (NULL
, so
),
150 g_slist_prepend (NULL
, snapped
),
151 FALSE
, _("Snap object to grid"));
154 cb_so_pull_to_front (SheetObject
*so
, SheetControl
*sc
)
156 cmd_object_raise (sc_wbc (sc
), so
, cmd_object_pull_to_front
);
159 cb_so_pull_forward (SheetObject
*so
, SheetControl
*sc
)
161 cmd_object_raise (sc_wbc (sc
), so
, cmd_object_pull_forward
);
164 cb_so_push_backward (SheetObject
*so
, SheetControl
*sc
)
166 cmd_object_raise (sc_wbc (sc
), so
, cmd_object_push_backward
);
169 cb_so_push_to_back (SheetObject
*so
, SheetControl
*sc
)
171 cmd_object_raise (sc_wbc (sc
), so
, cmd_object_push_to_back
);
174 cb_so_delete (SheetObject
*so
, SheetControl
*sc
)
176 cmd_objects_delete (sc_wbc (sc
),
177 go_slist_create (so
, NULL
), NULL
);
180 cb_so_print (SheetObject
*so
, SheetControl
*sc
)
182 GPtrArray
*a
= g_ptr_array_new ();
183 g_ptr_array_add (a
, so
);
184 gnm_print_so (sc_wbc (sc
), a
, NULL
);
185 g_ptr_array_unref (a
);
188 sheet_object_get_editor (SheetObject
*so
, SheetControl
*sc
)
192 g_return_if_fail (GNM_IS_SO (so
));
193 g_return_if_fail (SO_CLASS (so
));
194 g_return_if_fail (GNM_IS_SCG (sc
));
196 wbcg
= scg_wbcg ((SheetControlGUI
*)sc
);
198 if (wbcg
->edit_line
.guru
!= NULL
) {
199 GtkWidget
*w
= wbcg
->edit_line
.guru
;
200 wbc_gtk_detach_guru (wbcg
);
201 gtk_widget_destroy (w
);
204 if (SO_CLASS(so
)->user_config
)
205 SO_CLASS(so
)->user_config (so
, sc
);
208 cb_so_cut (SheetObject
*so
, SheetControl
*sc
)
210 gnm_app_clipboard_cut_copy_obj (sc_wbc (sc
), TRUE
, sc_view (sc
),
211 go_slist_create (so
, NULL
));
214 cb_so_copy (SheetObject
*so
, SheetControl
*sc
)
216 gnm_app_clipboard_cut_copy_obj (sc_wbc (sc
), FALSE
, sc_view (sc
),
217 go_slist_create (so
, NULL
));
221 sheet_object_can_print (SheetObject
const *so
)
223 g_return_val_if_fail (GNM_IS_SO (so
), FALSE
);
224 return (so
->flags
& SHEET_OBJECT_IS_VISIBLE
) &&
225 (so
->flags
& SHEET_OBJECT_PRINT
) &&
226 SO_CLASS (so
)->draw_cairo
!= NULL
;
230 sheet_object_can_resize (SheetObject
const *so
)
232 g_return_val_if_fail (GNM_IS_SO (so
), FALSE
);
233 return so
->flags
& SHEET_OBJECT_CAN_RESIZE
;
237 sheet_object_can_edit (SheetObject
const *so
)
239 g_return_val_if_fail (GNM_IS_SO (so
), FALSE
);
240 return so
->flags
& SHEET_OBJECT_CAN_EDIT
;
244 sheet_object_can_prop (SheetObject
const *so
)
246 g_return_val_if_fail (GNM_IS_SO (so
), FALSE
);
247 return (sheet_object_can_edit (so
) && (SO_CLASS(so
)->user_config
!= NULL
));
251 sheet_object_populate_menu_real (SheetObject
*so
, GPtrArray
*actions
)
254 if (so
->sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
255 static SheetObjectAction
const so_actions
[] = {
256 { "gtk-properties", NULL
, NULL
, 0, sheet_object_get_editor
, sheet_object_can_prop
},
257 { NULL
, NULL
, NULL
, 0, NULL
, NULL
},
258 { "edit-copy", N_("_Copy"), NULL
, 0, cb_so_copy
, NULL
},
260 for (i
= 0 ; i
< G_N_ELEMENTS (so_actions
); i
++)
261 g_ptr_array_add (actions
, (gpointer
) (so_actions
+ i
));
263 static SheetObjectAction
const so_actions
[] = {
264 { GTK_STOCK_PROPERTIES
, NULL
, NULL
, 0, sheet_object_get_editor
, sheet_object_can_prop
},
265 { NULL
, NULL
, NULL
, 0, NULL
, NULL
},
266 #warning "Two highly dubious icon names here"
267 { GTK_STOCK_LEAVE_FULLSCREEN
, N_("Size _& Position"), NULL
, 0, cb_so_size_position
, NULL
},
268 { GTK_STOCK_FULLSCREEN
, N_("_Snap to Grid"), NULL
, 0, cb_so_snap_to_grid
, NULL
},
269 { NULL
, N_("_Order"), NULL
, 1, NULL
, NULL
},
270 { NULL
, N_("Pul_l to Front"), NULL
, 0, cb_so_pull_to_front
, NULL
},
271 { NULL
, N_("Pull _Forward"), NULL
, 0, cb_so_pull_forward
, NULL
},
272 { NULL
, N_("Push _Backward"), NULL
, 0, cb_so_push_backward
, NULL
},
273 { NULL
, N_("Pus_h to Back"), NULL
, 0, cb_so_push_to_back
, NULL
},
274 { NULL
, NULL
, NULL
, -1, NULL
, NULL
},
275 { NULL
, NULL
, NULL
, 0, NULL
, NULL
},
276 { "edit-cut", N_("Cu_t"), NULL
, 0, cb_so_cut
, NULL
},
277 { "edit-copy", N_("_Copy"), NULL
, 0, cb_so_copy
, NULL
},
278 { "edit-delete", N_("_Delete"), NULL
, 0, cb_so_delete
, NULL
},
279 { NULL
, NULL
, NULL
, 0, NULL
, NULL
},
280 { "document-print", N_("Print"), NULL
, 0, cb_so_print
, sheet_object_can_print
},
282 for (i
= 0; i
< G_N_ELEMENTS (so_actions
); i
++)
283 g_ptr_array_add (actions
, (gpointer
) (so_actions
+ i
));
288 * sheet_object_populate_menu:
290 * @actions: (inout) (transfer full) (element-type SheetObjectAction): #GPtrArray
292 * Get a list of the actions that can be performed on @so
295 sheet_object_populate_menu (SheetObject
*so
, GPtrArray
*actions
)
297 g_return_if_fail (NULL
!= so
);
299 GNM_SO_CLASS (G_OBJECT_GET_CLASS(so
))->populate_menu (so
, actions
);
303 * sheet_objects_max_extent:
306 * Utility routine to calculate the maximum extent of objects in this sheet.
309 sheet_objects_max_extent (Sheet
*sheet
)
311 GnmCellPos max_pos
= { 0, 0 };
314 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
315 SheetObject
*so
= GNM_SO (ptr
->data
);
317 if (max_pos
.col
< so
->anchor
.cell_bound
.end
.col
)
318 max_pos
.col
= so
->anchor
.cell_bound
.end
.col
;
319 if (max_pos
.row
< so
->anchor
.cell_bound
.end
.row
)
320 max_pos
.row
= so
->anchor
.cell_bound
.end
.row
;
323 if (sheet
->max_object_extent
.col
!= max_pos
.col
||
324 sheet
->max_object_extent
.row
!= max_pos
.row
) {
325 sheet
->max_object_extent
= max_pos
;
326 sheet_scrollbar_config (sheet
);
331 sheet_object_set_name (SheetObject
*so
, const char *name
)
333 if (name
== so
->name
)
337 so
->name
= g_strdup (name
);
339 g_object_notify (G_OBJECT (so
), "name");
343 sheet_object_get_property (GObject
*obj
, guint param_id
,
344 GValue
*value
, GParamSpec
*pspec
)
346 SheetObject
*so
= GNM_SO (obj
);
350 g_value_set_string (value
, so
->name
);
353 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
359 sheet_object_set_property (GObject
*obj
, guint param_id
,
360 GValue
const *value
, GParamSpec
*pspec
)
362 SheetObject
*so
= GNM_SO (obj
);
366 sheet_object_set_name (so
, g_value_get_string (value
));
369 G_OBJECT_WARN_INVALID_PROPERTY_ID (obj
, param_id
, pspec
);
376 sheet_object_finalize (GObject
*object
)
378 SheetObject
*so
= GNM_SO (object
);
379 if (so
->sheet
!= NULL
)
380 sheet_object_clear_sheet (so
);
382 parent_klass
->finalize (object
);
386 sheet_object_init (GObject
*object
)
389 SheetObject
*so
= GNM_SO (object
);
392 so
->flags
= SHEET_OBJECT_IS_VISIBLE
| SHEET_OBJECT_PRINT
|
393 SHEET_OBJECT_CAN_RESIZE
| SHEET_OBJECT_CAN_EDIT
|
394 SHEET_OBJECT_MOVE_WITH_CELLS
| SHEET_OBJECT_SIZE_WITH_CELLS
;
396 /* Store the logical position as A1 */
397 so
->anchor
.cell_bound
.start
.col
= so
->anchor
.cell_bound
.start
.row
= 0;
398 so
->anchor
.cell_bound
.end
.col
= so
->anchor
.cell_bound
.end
.row
= 1;
399 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_UNKNOWN
;
401 for (i
= 4; i
-- > 0 ;)
402 so
->anchor
.offset
[i
] = 0.;
406 so_default_size (G_GNUC_UNUSED SheetObject
const *so
, double *width
, double *height
)
408 /* Provide some defaults (derived classes may want to override) */
414 sheet_object_class_init (GObjectClass
*klass
)
416 SheetObjectClass
*sheet_object_class
= GNM_SO_CLASS (klass
);
418 parent_klass
= g_type_class_peek_parent (klass
);
420 klass
->finalize
= sheet_object_finalize
;
421 klass
->get_property
= sheet_object_get_property
;
422 klass
->set_property
= sheet_object_set_property
;
424 sheet_object_class
->populate_menu
= sheet_object_populate_menu_real
;
425 sheet_object_class
->user_config
= NULL
;
426 sheet_object_class
->rubber_band_directly
= FALSE
;
427 sheet_object_class
->interactive
= FALSE
;
428 sheet_object_class
->default_size
= so_default_size
;
429 sheet_object_class
->xml_export_name
= NULL
;
430 sheet_object_class
->foreach_dep
= NULL
;
432 g_object_class_install_property
433 (klass
, SO_PROP_NAME
,
434 g_param_spec_string ("name", NULL
, NULL
, NULL
,
435 GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
437 signals
[BOUNDS_CHANGED
] = g_signal_new ("bounds-changed",
440 G_STRUCT_OFFSET (SheetObjectClass
, bounds_changed
),
442 g_cclosure_marshal_VOID__VOID
,
444 signals
[UNREALIZED
] = g_signal_new ("unrealized",
447 G_STRUCT_OFFSET (SheetObjectClass
, unrealized
),
449 g_cclosure_marshal_VOID__VOID
,
453 GSF_CLASS (SheetObject
, sheet_object
,
454 sheet_object_class_init
, sheet_object_init
,
458 * sheet_object_get_view:
460 * @container: #SheetObjectViewContainer
462 * Returns: (transfer none) (nullable): the found #SheetObjectView or %NULL.
465 sheet_object_get_view (SheetObject
const *so
, SheetObjectViewContainer
*container
)
469 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
471 for (l
= so
->realized_list
; l
; l
= l
->next
) {
472 SheetObjectView
*view
= GNM_SO_VIEW (l
->data
);
473 if (container
== g_object_get_qdata (G_OBJECT (view
), sov_container_quark
))
481 * sheet_object_update_bounds:
482 * @so: The sheet object
483 * @p: (nullable): A position marking the top left of the region
484 * needing relocation (default == A1)
486 * update the bounds of an object that intersects the region whose top left
487 * is @p. This is used when an objects position is anchored to cols/rows
488 * and they change position.
491 sheet_object_update_bounds (SheetObject
*so
, GnmCellPos
const *pos
)
493 gboolean is_hidden
= TRUE
;
496 g_return_if_fail (GNM_IS_SO (so
));
499 so
->anchor
.cell_bound
.end
.col
< pos
->col
&&
500 so
->anchor
.cell_bound
.end
.row
< pos
->row
)
503 if (so
->anchor
.mode
!= GNM_SO_ANCHOR_TWO_CELLS
) {
505 sheet_object_anchor_to_pts (&so
->anchor
, so
->sheet
, x
);
506 sheet_object_pts_to_anchor (&so
->anchor
, so
->sheet
, x
);
509 switch (so
->anchor
.mode
) {
511 case GNM_SO_ANCHOR_TWO_CELLS
:
512 /* Are all cols hidden ? */
513 end
= so
->anchor
.cell_bound
.end
.col
;
514 i
= so
->anchor
.cell_bound
.start
.col
;
515 while (i
<= end
&& is_hidden
)
516 is_hidden
&= sheet_col_is_hidden (so
->sheet
, i
++);
518 /* Are all rows hidden ? */
521 end
= so
->anchor
.cell_bound
.end
.row
;
522 i
= so
->anchor
.cell_bound
.start
.row
;
523 while (i
<= end
&& is_hidden
)
524 is_hidden
&= sheet_row_is_hidden (so
->sheet
, i
++);
527 case GNM_SO_ANCHOR_ONE_CELL
:
528 /* Should we really hide if the row or column is hidden? */
529 is_hidden
= sheet_col_is_hidden (so
->sheet
, so
->anchor
.cell_bound
.start
.col
) ||
530 sheet_row_is_hidden (so
->sheet
, so
->anchor
.cell_bound
.start
.row
);
532 case GNM_SO_ANCHOR_ABSOLUTE
:
537 so
->flags
&= ~SHEET_OBJECT_IS_VISIBLE
;
539 so
->flags
|= SHEET_OBJECT_IS_VISIBLE
;
541 g_signal_emit (so
, signals
[BOUNDS_CHANGED
], 0);
545 * sheet_object_get_sheet:
548 * A small utility to help keep the implementation of SheetObjects modular.
549 * Returns: (transfer none): the #Sheet owning the object.
552 sheet_object_get_sheet (SheetObject
const *so
)
554 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
560 cb_create_views (SheetObject
*so
)
562 g_object_set_data (G_OBJECT (so
), "create_view_handler", NULL
);
563 SHEET_FOREACH_CONTROL (so
->sheet
, view
, control
,
564 sc_object_create_view (control
, so
););
569 * sheet_object_set_sheet:
573 * Adds a reference to the object.
575 * Returns: %TRUE if there was a problem
578 sheet_object_set_sheet (SheetObject
*so
, Sheet
*sheet
)
580 g_return_val_if_fail (GNM_IS_SO (so
), TRUE
);
581 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
583 if (sheet
== so
->sheet
)
586 g_return_val_if_fail (so
->sheet
== NULL
, TRUE
);
587 g_return_val_if_fail (g_slist_find (sheet
->sheet_objects
, so
) == NULL
, TRUE
);
590 if (SO_CLASS (so
)->assign_to_sheet
&&
591 SO_CLASS (so
)->assign_to_sheet (so
, sheet
)) {
597 sheet
->sheet_objects
= g_slist_prepend (sheet
->sheet_objects
, so
);
598 /* Update object bounds for absolute and one cell anchored objects */
599 if (so
->anchor
.mode
!= GNM_SO_ANCHOR_TWO_CELLS
) {
601 sheet_object_anchor_to_pts (&so
->anchor
, sheet
, x
);
602 sheet_object_pts_to_anchor (&so
->anchor
, sheet
, x
);
604 /* FIXME : add a flag to sheet to have sheet_update do this */
605 sheet_objects_max_extent (sheet
);
607 if (NULL
== g_object_get_data (G_OBJECT (so
), "create_view_handler")) {
608 guint id
= g_idle_add ((GSourceFunc
) cb_create_views
, so
);
609 g_object_set_data (G_OBJECT (so
), "create_view_handler", GUINT_TO_POINTER (id
));
616 * sheet_object_clear_sheet:
619 * Removes @so from its container, unrealizes all views, disconects the
620 * associated data and unrefs the object
623 sheet_object_clear_sheet (SheetObject
*so
)
626 gpointer view_handler
;
628 g_return_if_fail (GNM_IS_SO (so
));
630 if (so
->sheet
== NULL
) /* already removed */
633 g_return_if_fail (IS_SHEET (so
->sheet
));
635 ptr
= g_slist_find (so
->sheet
->sheet_objects
, so
);
636 g_return_if_fail (ptr
!= NULL
);
638 /* clear any pending attempts to create views */
639 view_handler
= g_object_get_data (G_OBJECT (so
), "create_view_handler");
640 if (NULL
!= view_handler
) {
641 g_source_remove (GPOINTER_TO_UINT (view_handler
));
642 g_object_set_data (G_OBJECT (so
), "create_view_handler", NULL
);
645 while (so
->realized_list
!= NULL
) {
646 g_object_set_qdata (G_OBJECT (so
->realized_list
->data
), sov_so_quark
, NULL
);
647 g_object_unref (so
->realized_list
->data
);
648 so
->realized_list
= g_list_remove (so
->realized_list
, so
->realized_list
->data
);
651 g_signal_emit (so
, signals
[UNREALIZED
], 0);
653 if (SO_CLASS (so
)->remove_from_sheet
&&
654 SO_CLASS (so
)->remove_from_sheet (so
))
657 so
->sheet
->sheet_objects
= g_slist_remove_link (so
->sheet
->sheet_objects
, ptr
);
660 if (so
->anchor
.cell_bound
.end
.col
== so
->sheet
->max_object_extent
.col
&&
661 so
->anchor
.cell_bound
.end
.row
== so
->sheet
->max_object_extent
.row
)
662 sheet_objects_max_extent (so
->sheet
);
669 cb_sheet_object_invalidate_sheet (GnmDependent
*dep
, G_GNUC_UNUSED SheetObject
*so
, gpointer user
)
672 GnmExprRelocateInfo rinfo
;
673 GnmExprTop
const *texpr
;
674 gboolean save_invalidated
= sheet
->being_invalidated
;
675 gboolean dep_sheet_invalidated
= (dep
->sheet
== sheet
);
680 sheet
->being_invalidated
= TRUE
;
681 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_INVALIDATE_SHEET
;
682 texpr
= gnm_expr_top_relocate (dep
->texpr
, &rinfo
, FALSE
);
683 if (!texpr
&& dep_sheet_invalidated
) {
685 gnm_expr_top_ref (texpr
);
688 sheet
->being_invalidated
= save_invalidated
;
691 gboolean was_linked
= dependent_is_linked (dep
);
692 dependent_set_expr (dep
, texpr
);
693 gnm_expr_top_unref (texpr
);
694 if (dep_sheet_invalidated
)
697 dependent_link (dep
);
702 sheet_object_invalidate_sheet (SheetObject
*so
, Sheet
const *sheet
)
704 sheet_object_foreach_dep (so
, cb_sheet_object_invalidate_sheet
,
709 * sheet_object_foreach_dep:
711 * @func: (scope call): #SheetObjectForeachDepFunc
714 * Loops over each dependent contained in a sheet object and call the handler.
717 sheet_object_foreach_dep (SheetObject
*so
,
718 SheetObjectForeachDepFunc func
,
721 if (SO_CLASS (so
)->foreach_dep
)
722 SO_CLASS (so
)->foreach_dep (so
, func
, user
);
726 * sheet_object_new_view:
730 * Asks @so to create a view for @container.
731 * Returns: (transfer none): the new #SheetObjectView.
734 sheet_object_new_view (SheetObject
*so
, SheetObjectViewContainer
*container
)
736 SheetObjectView
*view
;
738 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
739 g_return_val_if_fail (NULL
!= container
, NULL
);
741 view
= sheet_object_get_view (so
, container
);
745 view
= SO_CLASS (so
)->new_view (so
, container
);
750 g_return_val_if_fail (GNM_IS_SO_VIEW (view
), NULL
);
752 /* Store some useful information */
753 g_object_set_qdata (G_OBJECT (view
), sov_so_quark
, so
);
754 g_object_set_qdata (G_OBJECT (view
), sov_container_quark
, container
);
755 so
->realized_list
= g_list_prepend (so
->realized_list
, view
);
756 sheet_object_update_bounds (so
, NULL
);
762 * sheet_object_draw_cairo:
764 * Draw a sheet object using cairo.
767 * We are assuming that the cairo context is set up so that the top
768 * left of the bounds is (0,0). Note that this
769 * is the real top left cell, not necessarily the cell with to which we are
774 sheet_object_draw_cairo (SheetObject
const *so
, cairo_t
*cr
, gboolean rtl
)
776 if (SO_CLASS (so
)->draw_cairo
) {
777 SheetObjectAnchor
const *anchor
;
778 double x
= 0., y
= 0., width
, height
, cell_width
, cell_height
;
779 anchor
= sheet_object_get_anchor (so
);
780 if (anchor
->mode
== GNM_SO_ANCHOR_ABSOLUTE
) {
781 x
= anchor
->offset
[0];
782 y
= anchor
->offset
[1];
783 if (sheet_object_can_resize (so
)) {
784 width
= anchor
->offset
[2];
785 height
= anchor
->offset
[3];
787 sheet_object_default_size ((SheetObject
*) so
, &width
, &height
);
791 cell_width
= sheet_col_get_distance_pts (so
->sheet
,
792 anchor
->cell_bound
.start
.col
,
793 anchor
->cell_bound
.start
.col
+ 1);
794 cell_height
= sheet_row_get_distance_pts (so
->sheet
,
795 anchor
->cell_bound
.start
.row
,
796 anchor
->cell_bound
.start
.row
+ 1);
797 x
= cell_width
* anchor
->offset
[0];
799 y
= cell_height
* anchor
->offset
[1];
800 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
801 cell_width
= sheet_col_get_distance_pts (so
->sheet
,
802 anchor
->cell_bound
.end
.col
,
803 anchor
->cell_bound
.end
.col
+ 1);
804 cell_height
= sheet_row_get_distance_pts (so
->sheet
,
805 anchor
->cell_bound
.end
.row
,
806 anchor
->cell_bound
.end
.row
+ 1);
809 x
= cell_width
* (1 - anchor
->offset
[2]);
811 if (sheet_object_can_resize (so
)) {
812 width
= sheet_col_get_distance_pts (so
->sheet
,
813 anchor
->cell_bound
.start
.col
,
814 anchor
->cell_bound
.end
.col
+ 1);
815 height
= sheet_row_get_distance_pts (so
->sheet
,
816 anchor
->cell_bound
.start
.row
,
817 anchor
->cell_bound
.end
.row
+ 1);
820 width
-= cell_width
* (1. - ((rtl
)? anchor
->offset
[0]: anchor
->offset
[2]));
821 height
-= cell_height
* (1 - anchor
->offset
[3]);
823 sheet_object_default_size ((SheetObject
*) so
, &width
, &height
);
825 if (sheet_object_can_resize (so
)) {
826 width
= anchor
->offset
[2];
827 height
= anchor
->offset
[3];
829 sheet_object_default_size ((SheetObject
*) so
, &width
, &height
);
831 x
= cell_width
* (1 - anchor
->offset
[0]) - width
;
835 /* we don't need to save/restore cairo, the caller must do it */
836 cairo_translate (cr
, x
, y
);
837 SO_CLASS (so
)->draw_cairo (so
, cr
, width
, height
);
842 sheet_object_draw_cairo_sized (SheetObject
const *so
, cairo_t
*cr
, double width
, double height
)
844 SO_CLASS (so
)->draw_cairo (so
, cr
, width
, height
);
848 * sheet_object_get_range:
849 * @so: the #SheetObject to query
851 * Returns: (transfer none): the #GnmRange used for @so.
854 sheet_object_get_range (SheetObject
const *so
)
856 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
858 return &so
->anchor
.cell_bound
;
862 * sheet_object_get_anchor:
865 * Returns: (transfer none): the #SheetObjectAnchor for @so.
867 SheetObjectAnchor
const *
868 sheet_object_get_anchor (SheetObject
const *so
)
870 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
876 sheet_object_set_anchor (SheetObject
*so
, SheetObjectAnchor
const *anchor
)
878 g_return_if_fail (GNM_IS_SO (so
));
880 so
->anchor
= *anchor
;
881 if (so
->sheet
!= NULL
) {
882 sheet_objects_max_extent (so
->sheet
);
883 sheet_object_update_bounds (so
, NULL
);
888 sheet_object_anchor_dup (SheetObjectAnchor
const *src
)
890 SheetObjectAnchor
*res
= g_memdup (src
, sizeof (SheetObjectAnchor
));
895 cell_offset_calc_pt (Sheet
const *sheet
, int i
, gboolean is_col
, double offset
)
897 ColRowInfo
const *cri
= sheet_colrow_get_info (sheet
, i
, is_col
);
898 return offset
* cri
->size_pts
;
902 * sheet_object_default_size:
903 * @so: The sheet object
904 * @w: (out): a ptr into which to store the default_width.
905 * @h: (out): a ptr into which to store the default_height.
907 * Measurements are in pts.
910 sheet_object_default_size (SheetObject
*so
, double *w
, double *h
)
912 g_return_if_fail (GNM_IS_SO (so
));
913 g_return_if_fail (w
!= NULL
);
914 g_return_if_fail (h
!= NULL
);
916 SO_CLASS (so
)->default_size (so
, w
, h
);
920 * sheet_object_position_pts_get:
921 * @so: The sheet object
922 * @coords: (out) (array fixed-size=4): coordinates
924 * Calculate the position of the object @so in pts from the logical position in
928 sheet_object_position_pts_get (SheetObject
const *so
, double *coords
)
930 g_return_if_fail (GNM_IS_SO (so
));
931 sheet_object_anchor_to_pts (&so
->anchor
, so
->sheet
, coords
);
935 sheet_object_anchor_to_pts (SheetObjectAnchor
const *anchor
,
936 Sheet
const *sheet
, double *res_pts
)
940 g_return_if_fail (res_pts
!= NULL
);
942 r
= &anchor
->cell_bound
;
944 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
945 res_pts
[0] = sheet_col_get_distance_pts (sheet
, 0,
947 res_pts
[1] = sheet_row_get_distance_pts (sheet
, 0,
949 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
950 res_pts
[2] = res_pts
[0] + sheet_col_get_distance_pts (sheet
,
951 r
->start
.col
, r
->end
.col
);
952 res_pts
[3] = res_pts
[1] + sheet_row_get_distance_pts (sheet
,
953 r
->start
.row
, r
->end
.row
);
955 res_pts
[0] += cell_offset_calc_pt (sheet
, r
->start
.col
,
956 TRUE
, anchor
->offset
[0]);
957 res_pts
[1] += cell_offset_calc_pt (sheet
, r
->start
.row
,
958 FALSE
, anchor
->offset
[1]);
959 res_pts
[2] += cell_offset_calc_pt (sheet
, r
->end
.col
,
960 TRUE
, anchor
->offset
[2]);
961 res_pts
[3] += cell_offset_calc_pt (sheet
, r
->end
.row
,
962 FALSE
, anchor
->offset
[3]);
964 res_pts
[0] += cell_offset_calc_pt (sheet
, r
->start
.col
,
965 TRUE
, anchor
->offset
[0]);
966 res_pts
[1] += cell_offset_calc_pt (sheet
, r
->start
.row
,
967 FALSE
, anchor
->offset
[1]);
968 res_pts
[2] = res_pts
[0] + anchor
->offset
[2];
969 res_pts
[3] = res_pts
[1] + anchor
->offset
[3];
972 res_pts
[0] = anchor
->offset
[0];
973 res_pts
[1] = anchor
->offset
[1];
974 res_pts
[2] = res_pts
[0] + anchor
->offset
[2];
975 res_pts
[3] = res_pts
[1] + anchor
->offset
[3];
980 sheet_object_pts_to_anchor (SheetObjectAnchor
*anchor
,
981 Sheet
const *sheet
, double const *res_pts
)
984 double x
, y
, tmp
= 0;
985 ColRowInfo
const *ci
;
986 /* find end column */
989 ci
= sheet_col_get_info (sheet
, col
);
992 if (res_pts
[0] <= x
+ tmp
)
996 } while (++col
< gnm_sheet_get_last_col (sheet
));
997 if (col
== gnm_sheet_get_last_col (sheet
)) {
998 /* not sure this will occur */
1002 anchor
->cell_bound
.start
.col
= col
;
1003 anchor
->offset
[0] = (anchor
->mode
== GNM_SO_ANCHOR_ABSOLUTE
)?
1004 res_pts
[0]: (res_pts
[0] - x
) / tmp
;
1005 /* find start row */
1008 ci
= sheet_row_get_info (sheet
, row
);
1011 if (res_pts
[1] <= y
+ tmp
)
1015 } while (++row
< gnm_sheet_get_last_row (sheet
));
1016 if (row
== gnm_sheet_get_last_row (sheet
)) {
1017 /* not sure this will occur */
1021 anchor
->cell_bound
.start
.row
= row
;
1022 anchor
->offset
[1] = (anchor
->mode
== GNM_SO_ANCHOR_ABSOLUTE
)?
1023 res_pts
[1]: (res_pts
[1] - y
) / tmp
;
1025 /* find end column */
1027 ci
= sheet_col_get_info (sheet
, col
);
1030 if (res_pts
[2] <= x
+ tmp
)
1034 } while (++col
< gnm_sheet_get_last_col (sheet
));
1035 if (col
== gnm_sheet_get_last_col (sheet
)) {
1036 /* not sure this will occur */
1040 anchor
->cell_bound
.end
.col
= col
;
1041 anchor
->offset
[2] = (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
)?
1042 (res_pts
[2] - x
) / tmp
: res_pts
[2] - res_pts
[0];
1045 ci
= sheet_row_get_info (sheet
, row
);
1048 if (res_pts
[3] <= y
+ tmp
)
1052 } while (++row
< gnm_sheet_get_last_row (sheet
));
1053 if (row
== gnm_sheet_get_last_row (sheet
)) {
1054 /* not sure this will occur */
1058 anchor
->cell_bound
.end
.row
= row
;
1059 anchor
->offset
[3] = (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
)?
1060 (res_pts
[3] - y
) / tmp
: res_pts
[3] - res_pts
[1];
1064 sheet_object_anchor_to_offset_pts (SheetObjectAnchor
const *anchor
,
1065 Sheet
const *sheet
, double *res_pts
)
1069 g_return_if_fail (res_pts
!= NULL
);
1071 r
= &anchor
->cell_bound
;
1073 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
1074 res_pts
[0] = cell_offset_calc_pt (sheet
, r
->start
.col
,
1075 TRUE
, anchor
->offset
[0]);
1076 res_pts
[1] = cell_offset_calc_pt (sheet
, r
->start
.row
,
1077 FALSE
, anchor
->offset
[1]);
1078 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
1079 res_pts
[2] = cell_offset_calc_pt (sheet
, r
->end
.col
,
1080 TRUE
, anchor
->offset
[2]);
1081 res_pts
[3] = cell_offset_calc_pt (sheet
, r
->end
.row
,
1082 FALSE
, anchor
->offset
[3]);
1088 clear_sheet (SheetObject
*so
, GOUndo
**pundo
)
1091 GOUndo
*u
= go_undo_binary_new
1094 (GOUndoBinaryFunc
)sheet_object_set_sheet
,
1095 (GFreeFunc
) g_object_unref
,
1097 *pundo
= go_undo_combine (*pundo
, u
);
1100 sheet_object_clear_sheet (so
);
1105 * sheet_objects_relocate:
1106 * @rinfo: details on what should be moved.
1107 * @update: Should we do the bound_update now, or leave it for later.
1108 * if FALSE honour the move_with_cells flag.
1109 * @pundo: (optional) (out): add dropped objects to ::objects
1111 * Uses the relocation info and the anchors to decide whether or not, and how
1112 * to relocate objects when the grid moves (eg ins/del col/row).
1115 sheet_objects_relocate (GnmExprRelocateInfo
const *rinfo
, gboolean update
,
1120 gboolean change_sheets
;
1122 g_return_if_fail (rinfo
!= NULL
);
1123 g_return_if_fail (IS_SHEET (rinfo
->origin_sheet
));
1124 g_return_if_fail (IS_SHEET (rinfo
->target_sheet
));
1126 dest
= rinfo
->origin
;
1127 range_translate (&dest
, rinfo
->target_sheet
,
1128 rinfo
->col_offset
, rinfo
->row_offset
);
1129 change_sheets
= (rinfo
->origin_sheet
!= rinfo
->target_sheet
);
1131 /* Clear the destination range on the target sheet */
1132 if (change_sheets
) {
1133 GSList
*copy
= g_slist_copy (rinfo
->target_sheet
->sheet_objects
);
1134 for (ptr
= copy
; ptr
!= NULL
; ptr
= ptr
->next
) {
1135 SheetObject
*so
= GNM_SO (ptr
->data
);
1136 GnmRange
const *r
= &so
->anchor
.cell_bound
;
1137 if (range_contains (&dest
, r
->start
.col
, r
->start
.row
)) {
1138 clear_sheet (so
, pundo
);
1141 g_slist_free (copy
);
1144 ptr
= rinfo
->origin_sheet
->sheet_objects
;
1145 for (; ptr
!= NULL
; ptr
= next
) {
1146 SheetObject
*so
= GNM_SO (ptr
->data
);
1147 GnmRange r
= so
->anchor
.cell_bound
;
1150 if ((so
->anchor
.mode
== GNM_SO_ANCHOR_ABSOLUTE
) ||
1151 (update
&& 0 == (so
->flags
& SHEET_OBJECT_MOVE_WITH_CELLS
)))
1153 if (range_contains (&rinfo
->origin
, r
.start
.col
, r
.start
.row
)) {
1154 /* FIXME : just moving the range is insufficent for all anchor types */
1155 /* Toss any objects that would be clipped. */
1156 if (range_translate (&r
, rinfo
->origin_sheet
,
1157 rinfo
->col_offset
, rinfo
->row_offset
)) {
1158 clear_sheet (so
, pundo
);
1161 so
->anchor
.cell_bound
= r
;
1163 if (change_sheets
) {
1165 sheet_object_clear_sheet (so
);
1166 sheet_object_set_sheet (so
, rinfo
->target_sheet
);
1167 g_object_unref (so
);
1169 sheet_object_update_bounds (so
, NULL
);
1170 } else if (!change_sheets
&&
1171 range_contains (&dest
, r
.start
.col
, r
.start
.row
)) {
1172 clear_sheet (so
, pundo
);
1177 sheet_objects_max_extent (rinfo
->origin_sheet
);
1179 sheet_objects_max_extent (rinfo
->target_sheet
);
1183 * sheet_objects_get:
1184 * @sheet: the sheet.
1185 * @r: (nullable): #GnmRange to look in
1186 * @t: The type of object to lookup, %G_TYPE_NONE for any.
1188 * Returns: (element-type SheetObject) (transfer container): a list
1189 * containing all objects of exactly the specified type (inheritence does
1190 * not count) that are completely contained in @r.
1193 sheet_objects_get (Sheet
const *sheet
, GnmRange
const *r
, GType t
)
1198 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1200 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
1201 GObject
*obj
= G_OBJECT (ptr
->data
);
1203 if (t
== G_TYPE_NONE
|| t
== G_OBJECT_TYPE (obj
)) {
1204 SheetObject
*so
= GNM_SO (obj
);
1205 if (r
== NULL
|| range_contained (&so
->anchor
.cell_bound
, r
))
1206 res
= g_slist_prepend (res
, so
);
1209 return g_slist_reverse (res
);
1213 * sheet_objects_clear:
1214 * @sheet: the sheet.
1215 * @r: (nullable): #GnmRange to look in
1217 * @pundo: (out) (nullable):
1219 * Removes the objects in the region.
1222 sheet_objects_clear (Sheet
const *sheet
, GnmRange
const *r
, GType t
,
1227 g_return_if_fail (IS_SHEET (sheet
));
1229 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= next
) {
1230 GObject
*obj
= G_OBJECT (ptr
->data
);
1232 if ((t
== G_TYPE_NONE
&& G_OBJECT_TYPE (obj
) != GNM_FILTER_COMBO_TYPE
)
1233 || t
== G_OBJECT_TYPE (obj
)) {
1234 SheetObject
*so
= GNM_SO (obj
);
1235 if (r
== NULL
|| range_contained (&so
->anchor
.cell_bound
, r
))
1236 clear_sheet (so
, pundo
);
1243 * @so: a #SheetObject to duplicate
1245 * Returns: (transfer full) (nullable): A copy of @so that is not attached
1249 sheet_object_dup (SheetObject
const *so
)
1251 SheetObject
*new_so
= NULL
;
1253 if (!SO_CLASS (so
)->copy
)
1256 new_so
= g_object_new (G_OBJECT_TYPE (so
), NULL
);
1258 g_return_val_if_fail (new_so
!= NULL
, NULL
);
1260 SO_CLASS (so
)->copy (new_so
, so
);
1261 new_so
->flags
= so
->flags
;
1262 new_so
->anchor
= so
->anchor
;
1268 cb_sheet_objects_dup (GnmDependent
*dep
, SheetObject
*so
, gpointer user
)
1271 Sheet
*dst
= sheet_object_get_sheet (so
);
1272 GnmExprTop
const *texpr
;
1277 texpr
= gnm_expr_top_relocate_sheet (dep
->texpr
, src
, dst
);
1278 if (texpr
!= dep
->texpr
) {
1279 gboolean was_linked
= dependent_is_linked (dep
);
1280 dependent_set_expr (dep
, texpr
);
1282 dependent_link (dep
);
1284 gnm_expr_top_unref (texpr
);
1289 * sheet_objects_dup:
1290 * @src: The source sheet to read the objects from
1291 * @dst: The destination sheet to attach the objects to
1292 * @range: (nullable): #GnmRange to look in
1294 * Clones the objects of the src sheet and attaches them into the dst sheet
1297 sheet_objects_dup (Sheet
const *src
, Sheet
*dst
, GnmRange
*range
)
1301 g_return_if_fail (IS_SHEET (dst
));
1302 g_return_if_fail (dst
->sheet_objects
== NULL
);
1304 for (list
= src
->sheet_objects
; list
!= NULL
; list
= list
->next
) {
1305 SheetObject
*so
= list
->data
;
1306 if (range
== NULL
|| range_overlap (range
, &so
->anchor
.cell_bound
)) {
1307 SheetObject
*new_so
= sheet_object_dup (so
);
1308 if (new_so
!= NULL
) {
1309 sheet_object_set_sheet (new_so
, dst
);
1310 sheet_object_foreach_dep (new_so
, cb_sheet_objects_dup
,
1312 g_object_unref (new_so
);
1317 dst
->sheet_objects
= g_slist_reverse (dst
->sheet_objects
);
1322 * sheet_object_direction_set:
1323 * @so: The sheet object that we are calculating the direction for
1324 * @coords: (in) (array fixed-size=4): array of coordinates in L,T,R,B order
1326 * Sets the object direction from the given the new coordinates
1327 * The original coordinates are assumed to be normalized (so that top
1328 * is above bottom and right is at the right of left)
1331 sheet_object_direction_set (SheetObject
*so
, gdouble
const *coords
)
1333 if (so
->anchor
.base
.direction
== GOD_ANCHOR_DIR_UNKNOWN
)
1336 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
1338 if (coords
[1] < coords
[3])
1339 so
->anchor
.base
.direction
|= GOD_ANCHOR_DIR_DOWN
;
1340 if (coords
[0] < coords
[2])
1341 so
->anchor
.base
.direction
|= GOD_ANCHOR_DIR_RIGHT
;
1345 * sheet_object_rubber_band_directly:
1348 * Returns: %TRUE if we should draw the object as we are laying it out on
1349 * an sheet. If %FALSE we draw a rectangle where the object is going to go
1352 sheet_object_rubber_band_directly (G_GNUC_UNUSED SheetObject
const *so
)
1358 * sheet_object_anchor_init:
1359 * @anchor: #SheetObjectAnchor
1360 * @r: (nullable): #GnmRange to look in
1361 * @offsets: (in) (array fixed-size=4) (nullable):
1362 * @direction: #GODrawingAnchorDir
1363 * @mode: #GnmSOAnchorMode
1365 * A utility routine to initialize an anchor. Useful in case we change fields
1366 * in the future and want to ensure that everything is initialized.
1369 sheet_object_anchor_init (SheetObjectAnchor
*anchor
,
1370 GnmRange
const *r
, const double *offsets
,
1371 GODrawingAnchorDir direction
,
1372 GnmSOAnchorMode mode
)
1377 static GnmRange
const defaultVal
= { { 0, 0 }, { 1, 1 } };
1380 anchor
->cell_bound
= *r
;
1382 if (offsets
== NULL
) {
1383 static double const defaultVal
[4] = { 0., 0., 0., 0. };
1384 offsets
= defaultVal
;
1386 for (i
= 4; i
-- > 0 ; )
1387 anchor
->offset
[i
] = offsets
[i
];
1389 anchor
->base
.direction
= direction
;
1390 anchor
->mode
= mode
;
1391 /* TODO : add sanity checking to handle offsets past edges of col/row */
1394 /*****************************************************************************/
1397 * sheet_object_get_stacking:
1400 * Returns: @so's position in the stack of sheet objects.
1403 sheet_object_get_stacking (SheetObject
*so
)
1405 g_return_val_if_fail (so
!= NULL
, 0);
1406 g_return_val_if_fail (so
->sheet
!= NULL
, 0);
1408 return g_slist_index (so
->sheet
->sheet_objects
, so
);
1412 sheet_object_adjust_stacking (SheetObject
*so
, gint offset
)
1415 GSList
**ptr
, *node
= NULL
;
1416 int i
, target
, cur
= 0;
1418 g_return_val_if_fail (so
!= NULL
, 0);
1419 g_return_val_if_fail (so
->sheet
!= NULL
, 0);
1421 for (ptr
= &so
->sheet
->sheet_objects
; *ptr
; ptr
= &(*ptr
)->next
, cur
++)
1422 if ((*ptr
)->data
== so
) {
1424 *ptr
= (*ptr
)->next
;
1428 g_return_val_if_fail (node
!= NULL
, 0);
1430 /* Start at the begining when moving things towards the front */
1432 ptr
= &so
->sheet
->sheet_objects
;
1437 for (target
= cur
- offset
; *ptr
&& i
< target
; ptr
= &(*ptr
)->next
)
1443 /* TODO : Move this to the container */
1444 for (l
= so
->realized_list
; l
; l
= l
->next
) {
1445 GocItem
*item
= GOC_ITEM (l
->data
);
1447 goc_item_raise (item
, offset
);
1449 goc_item_lower (item
, - offset
);
1455 sheet_object_set_anchor_mode (SheetObject
*so
, GnmSOAnchorMode
const *mode
)
1457 /* FIXME: adjust offsets according to the old and new modes */
1460 if (so
->anchor
.mode
== *mode
)
1462 sheet_object_anchor_to_pts (&so
->anchor
, so
->sheet
, pts
);
1463 so
->anchor
.mode
= *mode
;
1464 sheet_object_pts_to_anchor (&so
->anchor
, so
->sheet
, pts
);
1467 /*****************************************************************************/
1469 static GObjectClass
*view_parent_class
;
1472 sheet_object_view_set_bounds (SheetObjectView
*sov
,
1473 double const *coords
, gboolean visible
)
1475 SheetObjectViewClass
*klass
;
1477 g_return_if_fail (GNM_IS_SO_VIEW (sov
));
1478 klass
= GNM_SO_VIEW_GET_CLASS (sov
);
1479 if (NULL
!= klass
->set_bounds
)
1480 (klass
->set_bounds
) (sov
, coords
, visible
);
1484 * sheet_object_view_get_so:
1485 * @sov: #SheetObjectView
1487 * Returns: (transfer none): the #SheetObject owning @view.
1490 sheet_object_view_get_so (SheetObjectView
*view
)
1492 return g_object_get_qdata (G_OBJECT (view
), sov_so_quark
);
1496 sheet_object_view_enter_notify (GocItem
*item
, double x
, double y
)
1500 if (GNM_IS_PANE (item
->canvas
) && scg_wbcg (GNM_SIMPLE_CANVAS (item
->canvas
)->scg
)->new_object
) {
1501 GnmItemGrid
*grid
= GNM_PANE (item
->canvas
)->grid
;
1502 return GOC_ITEM_GET_CLASS (grid
)->enter_notify (GOC_ITEM (grid
), x
, y
);
1505 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1506 gnm_widget_set_cursor_type (GTK_WIDGET (item
->canvas
),
1507 (so
->flags
& SHEET_OBJECT_CAN_PRESS
) ? GDK_HAND2
: GDK_ARROW
);
1512 cb_so_menu_activate (GObject
*menu
, GocItem
*view
)
1514 SheetObjectAction
const *a
= g_object_get_data (menu
, "action");
1517 SheetObject
*so
= sheet_object_view_get_so (GNM_SO_VIEW (view
));
1518 gpointer data
= g_object_get_data (G_OBJECT (view
->canvas
), "sheet-control");
1521 data
= GNM_SIMPLE_CANVAS (view
->canvas
)->scg
;
1523 (a
->func
) (so
, GNM_SHEET_CONTROL (data
));
1528 cb_ptr_array_free (GPtrArray
*actions
)
1530 g_ptr_array_free (actions
, TRUE
);
1534 * sheet_object_build_menu:
1535 * @view: #SheetObjectView
1536 * @actions: (element-type SheetObjectAction): array of actions.
1537 * @i: index of first action to add in the array.
1539 * Builds the contextual menu for @view.
1540 * Returns: (transfer full): the newly constructed #GtkMenu.
1543 sheet_object_build_menu (SheetObjectView
*view
,
1544 GPtrArray
const *actions
, unsigned *i
)
1546 SheetObjectAction
const *a
;
1547 GtkWidget
*item
, *menu
= gtk_menu_new ();
1549 while (*i
< actions
->len
) {
1550 a
= g_ptr_array_index (actions
, *i
);
1554 if (a
->icon
!= NULL
) {
1555 if (a
->label
!= NULL
) {
1556 item
= gtk_image_menu_item_new_with_mnemonic (_(a
->label
));
1557 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item
),
1558 gtk_image_new_from_icon_name (a
->icon
, GTK_ICON_SIZE_MENU
));
1560 item
= gtk_image_menu_item_new_from_stock (a
->icon
, NULL
);
1561 } else if (a
->label
!= NULL
)
1562 item
= gtk_menu_item_new_with_mnemonic (_(a
->label
));
1564 item
= gtk_separator_menu_item_new ();
1566 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item
),
1567 sheet_object_build_menu (view
, actions
, i
));
1568 else if (a
->label
!= NULL
|| a
->icon
!= NULL
) { /* not a separator or menu */
1569 g_object_set_data (G_OBJECT (item
), "action", (gpointer
)a
);
1570 g_signal_connect_object (G_OBJECT (item
), "activate",
1571 G_CALLBACK (cb_so_menu_activate
), view
, 0);
1572 gtk_widget_set_sensitive (item
, a
->enable_func
== NULL
1573 || a
->enable_func (sheet_object_view_get_so (view
)));
1575 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1581 sheet_object_view_button_pressed (GocItem
*item
, int button
, double x
, double y
)
1585 if (GNM_IS_PANE (item
->canvas
)) {
1586 if (scg_wbcg (GNM_SIMPLE_CANVAS (item
->canvas
)->scg
)->new_object
) {
1587 GnmItemGrid
*grid
= GNM_PANE (item
->canvas
)->grid
;
1588 return GOC_ITEM_GET_CLASS (grid
)->button_pressed (GOC_ITEM (grid
), button
, x
, y
);
1594 pane
= GNM_PANE (item
->canvas
);
1595 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1597 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
1598 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
1599 /* cb_sheet_object_widget_canvas_event calls even if selected */
1600 if (NULL
== g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
)) {
1601 SheetObjectClass
*soc
=
1602 G_TYPE_INSTANCE_GET_CLASS (so
, GNM_SO_TYPE
, SheetObjectClass
);
1603 GdkEventButton
*event
= (GdkEventButton
*) goc_canvas_get_cur_event (item
->canvas
);
1605 if (soc
->interactive
&& button
!= 3)
1608 if (!(event
->state
& GDK_SHIFT_MASK
))
1609 scg_object_unselect (pane
->simple
.scg
, NULL
);
1610 scg_object_select (pane
->simple
.scg
, so
);
1611 if (NULL
== g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
))
1612 return FALSE
; /* protected ? */
1616 gnm_pane_object_start_resize (pane
, button
, x
, y
, so
, 8, FALSE
);
1618 gnm_pane_display_object_menu (pane
, so
, goc_canvas_get_cur_event (item
->canvas
));
1621 GPtrArray
*actions
= g_ptr_array_new ();
1625 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1626 sheet_object_populate_menu (so
, actions
);
1628 if (actions
->len
== 0) {
1629 g_ptr_array_free (actions
, TRUE
);
1633 menu
= sheet_object_build_menu
1634 (sheet_object_get_view (so
, (SheetObjectViewContainer
*) item
->canvas
),
1636 g_object_set_data_full (G_OBJECT (menu
), "actions", actions
,
1637 (GDestroyNotify
) cb_ptr_array_free
);
1638 gtk_widget_show_all (menu
);
1639 gnumeric_popup_menu (GTK_MENU (menu
),
1640 goc_canvas_get_cur_event (item
->canvas
));
1647 sheet_object_view_button2_pressed (GocItem
*item
, int button
, double x
, double y
)
1649 if (button
== 1 && !GNM_IS_PANE (item
->canvas
)) {
1650 SheetControl
*sc
= GNM_SHEET_CONTROL (g_object_get_data (G_OBJECT (item
->canvas
), "sheet-control"));
1651 SheetObject
*so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1653 if (sc
&& sheet_object_can_edit (so
))
1654 sheet_object_get_editor (so
, sc
);
1660 sheet_object_view_finalize (GObject
*obj
)
1662 SheetObject
*so
= (SheetObject
*) g_object_get_qdata (obj
, sov_so_quark
);
1664 so
->realized_list
= g_list_remove (so
->realized_list
, obj
);
1665 view_parent_class
->finalize (obj
);
1669 sheet_object_view_class_init (GocItemClass
*item_klass
)
1671 GObjectClass
*obj_klass
= (GObjectClass
*) item_klass
;
1672 view_parent_class
= g_type_class_peek_parent (item_klass
);
1674 obj_klass
->finalize
= sheet_object_view_finalize
;
1676 item_klass
->enter_notify
= sheet_object_view_enter_notify
;
1677 item_klass
->button_pressed
= sheet_object_view_button_pressed
;
1678 item_klass
->button2_pressed
= sheet_object_view_button2_pressed
;
1681 GSF_CLASS (SheetObjectView
, sheet_object_view
,
1682 sheet_object_view_class_init
, NULL
,
1685 /*****************************************************************************/
1688 sheet_object_imageable_get_type (void)
1690 static GType type
= 0;
1693 static GTypeInfo
const type_info
= {
1694 sizeof (SheetObjectImageableIface
), /* class_size */
1695 NULL
, /* base_init */
1696 NULL
, /* base_finalize */
1697 NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
1700 type
= g_type_register_static (G_TYPE_INTERFACE
,
1701 "SheetObjectImageable", &type_info
, 0);
1707 #define GNM_SO_IMAGEABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GNM_SO_IMAGEABLE_TYPE, SheetObjectImageableIface))
1710 sheet_object_get_target_list (SheetObject
const *so
)
1712 if (!GNM_IS_SO_IMAGEABLE (so
))
1715 return GNM_SO_IMAGEABLE_CLASS (so
)->get_target_list (so
);
1719 sheet_object_write_image (SheetObject
const *so
, char const *format
, double resolution
,
1720 GsfOutput
*output
, GError
**err
)
1722 g_return_if_fail (GNM_IS_SO_IMAGEABLE (so
));
1724 GNM_SO_IMAGEABLE_CLASS (so
)->write_image (so
, format
, resolution
,
1729 /*****************************************************************************/
1732 sheet_object_exportable_get_type (void)
1734 static GType type
= 0;
1737 static GTypeInfo
const type_info
= {
1738 sizeof (SheetObjectExportableIface
), /* class_size */
1739 NULL
, /* base_init */
1740 NULL
, /* base_finalize */
1741 NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
1744 type
= g_type_register_static (G_TYPE_INTERFACE
,
1745 "SheetObjectExportable", &type_info
, 0);
1751 #define GNM_SO_EXPORTABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GNM_SO_EXPORTABLE_TYPE, SheetObjectExportableIface))
1754 sheet_object_exportable_get_target_list (SheetObject
const *so
)
1756 if (!GNM_IS_SO_EXPORTABLE (so
))
1759 return GNM_SO_EXPORTABLE_CLASS (so
)->get_target_list (so
);
1763 sheet_object_write_object (SheetObject
const *so
, char const *format
,
1764 GsfOutput
*output
, GError
**err
,
1765 GnmConventions
const *convs
)
1769 g_return_if_fail (GNM_IS_SO_EXPORTABLE (so
));
1771 locale
= gnm_push_C_locale ();
1772 GNM_SO_EXPORTABLE_CLASS (so
)->
1773 write_object (so
, format
, output
, err
, convs
);
1774 gnm_pop_C_locale (locale
);
1778 * sheet_object_move_undo:
1779 * @objects: (element-type SheetObject):
1782 * Returns: (transfer full): the newly allocated #GOUndo.
1785 sheet_object_move_undo (GSList
*objects
, gboolean objects_created
)
1787 GOUndo
*undo
= NULL
;
1788 GSList
*objs
= objects
;
1790 g_return_val_if_fail (NULL
!= objects
, NULL
);
1792 for (; objs
; objs
= objs
->next
) {
1793 SheetObject
*obj
= objs
->data
;
1794 SheetObjectAnchor
*tmp
;
1796 if (objects_created
) {
1797 undo
= go_undo_combine
1800 (g_object_ref (obj
),
1801 (GOUndoUnaryFunc
) sheet_object_clear_sheet
,
1802 (GFreeFunc
) g_object_unref
));
1805 tmp
= g_new (SheetObjectAnchor
, 1);
1806 *tmp
= *sheet_object_get_anchor (obj
);
1807 undo
= go_undo_combine
1808 (undo
, go_undo_binary_new
1809 (g_object_ref (obj
), tmp
,
1810 (GOUndoBinaryFunc
) sheet_object_set_anchor
,
1811 (GFreeFunc
) g_object_unref
,
1812 (GFreeFunc
) g_free
));
1818 * sheet_object_move_do:
1819 * @objects: (element-type SheetObject):
1820 * @anchors: (element-type SheetObjectAnchor):
1823 * Returns: (transfer full): the newly allocated #GOUndo.
1826 sheet_object_move_do (GSList
*objects
, GSList
*anchors
,
1827 gboolean objects_created
)
1829 GOUndo
*undo
= NULL
;
1830 GSList
*objs
= objects
, *anchs
= anchors
;
1832 g_return_val_if_fail (NULL
!= objects
, NULL
);
1833 g_return_val_if_fail (NULL
!= anchors
, NULL
);
1834 g_return_val_if_fail (g_slist_length (objects
)
1835 == g_slist_length (anchors
), NULL
);
1837 for (; objs
&& anchs
; objs
= objs
->next
, anchs
= anchs
->next
) {
1838 SheetObject
*obj
= objs
->data
;
1839 SheetObjectAnchor
*anch
= anchs
->data
;
1840 SheetObjectAnchor
*tmp
;
1842 if (objects_created
) {
1843 undo
= go_undo_combine
1846 (g_object_ref (obj
),
1847 sheet_object_get_sheet (obj
),
1848 (GOUndoBinaryFunc
) sheet_object_set_sheet
,
1849 (GFreeFunc
) g_object_unref
,
1852 tmp
= g_new (SheetObjectAnchor
, 1);
1854 undo
= go_undo_combine
1856 (g_object_ref (obj
), tmp
,
1857 (GOUndoBinaryFunc
) sheet_object_set_anchor
,
1858 (GFreeFunc
) g_object_unref
,
1859 (GFreeFunc
) g_free
), undo
);
1865 /*****************************************************************************/
1868 * sheet_objects_init: (skip)
1871 sheet_objects_init (void)
1877 GNM_GO_DATA_SCALAR_TYPE
;
1878 GNM_GO_DATA_VECTOR_TYPE
;
1879 GNM_GO_DATA_MATRIX_TYPE
;
1880 GNM_CELL_COMMENT_TYPE
;
1882 sheet_object_widget_register ();
1883 sov_so_quark
= g_quark_from_static_string ("SheetObject");
1884 sov_container_quark
= g_quark_from_static_string ("SheetObjectViewContainer");