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"
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 high 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): 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: An optional 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 (SheetObject
const *so
)
850 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
852 return &so
->anchor
.cell_bound
;
856 * sheet_object_get_anchor:
859 * Returns: (transfer none): the #SheetObjectAnchor for @so.
861 SheetObjectAnchor
const *
862 sheet_object_get_anchor (SheetObject
const *so
)
864 g_return_val_if_fail (GNM_IS_SO (so
), NULL
);
870 sheet_object_set_anchor (SheetObject
*so
, SheetObjectAnchor
const *anchor
)
872 g_return_if_fail (GNM_IS_SO (so
));
874 so
->anchor
= *anchor
;
875 if (so
->sheet
!= NULL
) {
876 sheet_objects_max_extent (so
->sheet
);
877 sheet_object_update_bounds (so
, NULL
);
882 sheet_object_anchor_dup (SheetObjectAnchor
const *src
)
884 SheetObjectAnchor
*res
= g_memdup (src
, sizeof (SheetObjectAnchor
));
889 cell_offset_calc_pt (Sheet
const *sheet
, int i
, gboolean is_col
, double offset
)
891 ColRowInfo
const *cri
= sheet_colrow_get_info (sheet
, i
, is_col
);
892 return offset
* cri
->size_pts
;
896 * sheet_object_default_size:
897 * @so: The sheet object
898 * @w: a ptr into which to store the default_width.
899 * @h: a ptr into which to store the default_height.
901 * Measurements are in pts.
904 sheet_object_default_size (SheetObject
*so
, double *w
, double *h
)
906 g_return_if_fail (GNM_IS_SO (so
));
907 g_return_if_fail (w
!= NULL
);
908 g_return_if_fail (h
!= NULL
);
910 SO_CLASS (so
)->default_size (so
, w
, h
);
914 * sheet_object_position_pts_get:
915 * @so: The sheet object
916 * @coords: array of 4 doubles
918 * Calculate the position of the object @so in pts from the logical position in
922 sheet_object_position_pts_get (SheetObject
const *so
, double *coords
)
924 g_return_if_fail (GNM_IS_SO (so
));
925 sheet_object_anchor_to_pts (&so
->anchor
, so
->sheet
, coords
);
929 sheet_object_anchor_to_pts (SheetObjectAnchor
const *anchor
,
930 Sheet
const *sheet
, double *res_pts
)
934 g_return_if_fail (res_pts
!= NULL
);
936 r
= &anchor
->cell_bound
;
938 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
939 res_pts
[0] = sheet_col_get_distance_pts (sheet
, 0,
941 res_pts
[1] = sheet_row_get_distance_pts (sheet
, 0,
943 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
944 res_pts
[2] = res_pts
[0] + sheet_col_get_distance_pts (sheet
,
945 r
->start
.col
, r
->end
.col
);
946 res_pts
[3] = res_pts
[1] + sheet_row_get_distance_pts (sheet
,
947 r
->start
.row
, r
->end
.row
);
949 res_pts
[0] += cell_offset_calc_pt (sheet
, r
->start
.col
,
950 TRUE
, anchor
->offset
[0]);
951 res_pts
[1] += cell_offset_calc_pt (sheet
, r
->start
.row
,
952 FALSE
, anchor
->offset
[1]);
953 res_pts
[2] += cell_offset_calc_pt (sheet
, r
->end
.col
,
954 TRUE
, anchor
->offset
[2]);
955 res_pts
[3] += cell_offset_calc_pt (sheet
, r
->end
.row
,
956 FALSE
, anchor
->offset
[3]);
958 res_pts
[0] += cell_offset_calc_pt (sheet
, r
->start
.col
,
959 TRUE
, anchor
->offset
[0]);
960 res_pts
[1] += cell_offset_calc_pt (sheet
, r
->start
.row
,
961 FALSE
, anchor
->offset
[1]);
962 res_pts
[2] = res_pts
[0] + anchor
->offset
[2];
963 res_pts
[3] = res_pts
[1] + anchor
->offset
[3];
966 res_pts
[0] = anchor
->offset
[0];
967 res_pts
[1] = anchor
->offset
[1];
968 res_pts
[2] = res_pts
[0] + anchor
->offset
[2];
969 res_pts
[3] = res_pts
[1] + anchor
->offset
[3];
974 sheet_object_pts_to_anchor (SheetObjectAnchor
*anchor
,
975 Sheet
const *sheet
, double const *res_pts
)
978 double x
, y
, tmp
= 0;
979 ColRowInfo
const *ci
;
980 /* find end column */
983 ci
= sheet_col_get_info (sheet
, col
);
986 if (res_pts
[0] <= x
+ tmp
)
990 } while (++col
< gnm_sheet_get_last_col (sheet
));
991 if (col
== gnm_sheet_get_last_col (sheet
)) {
992 /* not sure this will occur */
996 anchor
->cell_bound
.start
.col
= col
;
997 anchor
->offset
[0] = (anchor
->mode
== GNM_SO_ANCHOR_ABSOLUTE
)?
998 res_pts
[0]: (res_pts
[0] - x
) / tmp
;
1002 ci
= sheet_row_get_info (sheet
, row
);
1005 if (res_pts
[1] <= y
+ tmp
)
1009 } while (++row
< gnm_sheet_get_last_row (sheet
));
1010 if (row
== gnm_sheet_get_last_row (sheet
)) {
1011 /* not sure this will occur */
1015 anchor
->cell_bound
.start
.row
= row
;
1016 anchor
->offset
[1] = (anchor
->mode
== GNM_SO_ANCHOR_ABSOLUTE
)?
1017 res_pts
[1]: (res_pts
[1] - y
) / tmp
;
1019 /* find end column */
1021 ci
= sheet_col_get_info (sheet
, col
);
1024 if (res_pts
[2] <= x
+ tmp
)
1028 } while (++col
< gnm_sheet_get_last_col (sheet
));
1029 if (col
== gnm_sheet_get_last_col (sheet
)) {
1030 /* not sure this will occur */
1034 anchor
->cell_bound
.end
.col
= col
;
1035 anchor
->offset
[2] = (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
)?
1036 (res_pts
[2] - x
) / tmp
: res_pts
[2] - res_pts
[0];
1039 ci
= sheet_row_get_info (sheet
, row
);
1042 if (res_pts
[3] <= y
+ tmp
)
1046 } while (++row
< gnm_sheet_get_last_row (sheet
));
1047 if (row
== gnm_sheet_get_last_row (sheet
)) {
1048 /* not sure this will occur */
1052 anchor
->cell_bound
.end
.row
= row
;
1053 anchor
->offset
[3] = (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
)?
1054 (res_pts
[3] - y
) / tmp
: res_pts
[3] - res_pts
[1];
1058 sheet_object_anchor_to_offset_pts (SheetObjectAnchor
const *anchor
,
1059 Sheet
const *sheet
, double *res_pts
)
1063 g_return_if_fail (res_pts
!= NULL
);
1065 r
= &anchor
->cell_bound
;
1067 if (anchor
->mode
!= GNM_SO_ANCHOR_ABSOLUTE
) {
1068 res_pts
[0] = cell_offset_calc_pt (sheet
, r
->start
.col
,
1069 TRUE
, anchor
->offset
[0]);
1070 res_pts
[1] = cell_offset_calc_pt (sheet
, r
->start
.row
,
1071 FALSE
, anchor
->offset
[1]);
1072 if (anchor
->mode
== GNM_SO_ANCHOR_TWO_CELLS
) {
1073 res_pts
[2] = cell_offset_calc_pt (sheet
, r
->end
.col
,
1074 TRUE
, anchor
->offset
[2]);
1075 res_pts
[3] = cell_offset_calc_pt (sheet
, r
->end
.row
,
1076 FALSE
, anchor
->offset
[3]);
1082 clear_sheet (SheetObject
*so
, GOUndo
**pundo
)
1085 GOUndo
*u
= go_undo_binary_new
1088 (GOUndoBinaryFunc
)sheet_object_set_sheet
,
1089 (GFreeFunc
) g_object_unref
,
1091 *pundo
= go_undo_combine (*pundo
, u
);
1094 sheet_object_clear_sheet (so
);
1099 * sheet_objects_relocate:
1100 * @rinfo: details on what should be moved.
1101 * @update: Should we do the bound_update now, or leave it for later.
1102 * if FALSE honour the move_with_cells flag.
1103 * @pundo: if non-NULL add dropped objects to ::objects
1105 * Uses the relocation info and the anchors to decide whether or not, and how
1106 * to relocate objects when the grid moves (eg ins/del col/row).
1109 sheet_objects_relocate (GnmExprRelocateInfo
const *rinfo
, gboolean update
,
1114 gboolean change_sheets
;
1116 g_return_if_fail (rinfo
!= NULL
);
1117 g_return_if_fail (IS_SHEET (rinfo
->origin_sheet
));
1118 g_return_if_fail (IS_SHEET (rinfo
->target_sheet
));
1120 dest
= rinfo
->origin
;
1121 range_translate (&dest
, rinfo
->target_sheet
,
1122 rinfo
->col_offset
, rinfo
->row_offset
);
1123 change_sheets
= (rinfo
->origin_sheet
!= rinfo
->target_sheet
);
1125 /* Clear the destination range on the target sheet */
1126 if (change_sheets
) {
1127 GSList
*copy
= g_slist_copy (rinfo
->target_sheet
->sheet_objects
);
1128 for (ptr
= copy
; ptr
!= NULL
; ptr
= ptr
->next
) {
1129 SheetObject
*so
= GNM_SO (ptr
->data
);
1130 GnmRange
const *r
= &so
->anchor
.cell_bound
;
1131 if (range_contains (&dest
, r
->start
.col
, r
->start
.row
)) {
1132 clear_sheet (so
, pundo
);
1135 g_slist_free (copy
);
1138 ptr
= rinfo
->origin_sheet
->sheet_objects
;
1139 for (; ptr
!= NULL
; ptr
= next
) {
1140 SheetObject
*so
= GNM_SO (ptr
->data
);
1141 GnmRange r
= so
->anchor
.cell_bound
;
1144 if ((so
->anchor
.mode
== GNM_SO_ANCHOR_ABSOLUTE
) ||
1145 (update
&& 0 == (so
->flags
& SHEET_OBJECT_MOVE_WITH_CELLS
)))
1147 if (range_contains (&rinfo
->origin
, r
.start
.col
, r
.start
.row
)) {
1148 /* FIXME : just moving the range is insufficent for all anchor types */
1149 /* Toss any objects that would be clipped. */
1150 if (range_translate (&r
, rinfo
->origin_sheet
,
1151 rinfo
->col_offset
, rinfo
->row_offset
)) {
1152 clear_sheet (so
, pundo
);
1155 so
->anchor
.cell_bound
= r
;
1157 if (change_sheets
) {
1159 sheet_object_clear_sheet (so
);
1160 sheet_object_set_sheet (so
, rinfo
->target_sheet
);
1161 g_object_unref (so
);
1163 sheet_object_update_bounds (so
, NULL
);
1164 } else if (!change_sheets
&&
1165 range_contains (&dest
, r
.start
.col
, r
.start
.row
)) {
1166 clear_sheet (so
, pundo
);
1171 sheet_objects_max_extent (rinfo
->origin_sheet
);
1173 sheet_objects_max_extent (rinfo
->target_sheet
);
1177 * sheet_objects_get:
1178 * @sheet: the sheet.
1179 * @r: an optional range to look in
1180 * @t: The type of object to lookup
1182 * Returns: (element-type SheetObject) (transfer container): a list of which
1183 * the caller must free (just the list not the content).
1184 * Containing all objects of exactly the specified type (inheritence does not count)
1185 * that are completely contained by @r.
1188 sheet_objects_get (Sheet
const *sheet
, GnmRange
const *r
, GType t
)
1193 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1195 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
) {
1196 GObject
*obj
= G_OBJECT (ptr
->data
);
1198 if (t
== G_TYPE_NONE
|| t
== G_OBJECT_TYPE (obj
)) {
1199 SheetObject
*so
= GNM_SO (obj
);
1200 if (r
== NULL
|| range_contained (&so
->anchor
.cell_bound
, r
))
1201 res
= g_slist_prepend (res
, so
);
1204 return g_slist_reverse (res
);
1208 * sheet_objects_clear:
1209 * @sheet: the sheet.
1210 * @r: an optional range to look in
1212 * removes the objects in the region.
1215 sheet_objects_clear (Sheet
const *sheet
, GnmRange
const *r
, GType t
,
1220 g_return_if_fail (IS_SHEET (sheet
));
1222 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= next
) {
1223 GObject
*obj
= G_OBJECT (ptr
->data
);
1225 if ((t
== G_TYPE_NONE
&& G_OBJECT_TYPE (obj
) != GNM_FILTER_COMBO_TYPE
)
1226 || t
== G_OBJECT_TYPE (obj
)) {
1227 SheetObject
*so
= GNM_SO (obj
);
1228 if (r
== NULL
|| range_contained (&so
->anchor
.cell_bound
, r
))
1229 clear_sheet (so
, pundo
);
1236 * @so: a #SheetObject to duplicate
1238 * Returns: (transfer full): A copy of @so that is not attached to a sheet.
1239 * Caller is responsible for the reference.
1242 sheet_object_dup (SheetObject
const *so
)
1244 SheetObject
*new_so
= NULL
;
1246 if (!SO_CLASS (so
)->copy
)
1249 new_so
= g_object_new (G_OBJECT_TYPE (so
), NULL
);
1251 g_return_val_if_fail (new_so
!= NULL
, NULL
);
1253 SO_CLASS (so
)->copy (new_so
, so
);
1254 new_so
->flags
= so
->flags
;
1255 new_so
->anchor
= so
->anchor
;
1261 cb_sheet_objects_dup (GnmDependent
*dep
, SheetObject
*so
, gpointer user
)
1264 Sheet
*dst
= sheet_object_get_sheet (so
);
1265 GnmExprTop
const *texpr
;
1270 texpr
= gnm_expr_top_relocate_sheet (dep
->texpr
, src
, dst
);
1271 if (texpr
!= dep
->texpr
) {
1272 gboolean was_linked
= dependent_is_linked (dep
);
1273 dependent_set_expr (dep
, texpr
);
1275 dependent_link (dep
);
1277 gnm_expr_top_unref (texpr
);
1282 * sheet_objects_dup:
1283 * @src: The source sheet to read the objects from
1284 * @dst: The destination sheet to attach the objects to
1285 * @range: Optionally NULL region of interest
1287 * Clones the objects of the src sheet and attaches them into the dst sheet
1290 sheet_objects_dup (Sheet
const *src
, Sheet
*dst
, GnmRange
*range
)
1294 g_return_if_fail (IS_SHEET (dst
));
1295 g_return_if_fail (dst
->sheet_objects
== NULL
);
1297 for (list
= src
->sheet_objects
; list
!= NULL
; list
= list
->next
) {
1298 SheetObject
*so
= list
->data
;
1299 if (range
== NULL
|| range_overlap (range
, &so
->anchor
.cell_bound
)) {
1300 SheetObject
*new_so
= sheet_object_dup (so
);
1301 if (new_so
!= NULL
) {
1302 sheet_object_set_sheet (new_so
, dst
);
1303 sheet_object_foreach_dep (new_so
, cb_sheet_objects_dup
,
1305 g_object_unref (new_so
);
1310 dst
->sheet_objects
= g_slist_reverse (dst
->sheet_objects
);
1315 * sheet_object_direction_set:
1316 * @so: The sheet object that we are calculating the direction for
1317 * @coords: array of coordinates in L,T,R,B order
1319 * Sets the object direction from the given the new coordinates
1320 * The original coordinates are assumed to be normalized (so that top
1321 * is above bottom and right is at the right of left)
1324 sheet_object_direction_set (SheetObject
*so
, gdouble
const *coords
)
1326 if (so
->anchor
.base
.direction
== GOD_ANCHOR_DIR_UNKNOWN
)
1329 so
->anchor
.base
.direction
= GOD_ANCHOR_DIR_NONE_MASK
;
1331 if (coords
[1] < coords
[3])
1332 so
->anchor
.base
.direction
|= GOD_ANCHOR_DIR_DOWN
;
1333 if (coords
[0] < coords
[2])
1334 so
->anchor
.base
.direction
|= GOD_ANCHOR_DIR_RIGHT
;
1338 * sheet_object_rubber_band_directly:
1341 * Returns TRUE if we should draw the object as we are laying it out on
1342 * an sheet. If FLASE we draw a rectangle where the object is going to go
1347 sheet_object_rubber_band_directly (G_GNUC_UNUSED SheetObject
const *so
)
1353 * sheet_object_anchor_init:
1354 * @anchor: #SheetObjectAnchor
1355 * @cell_bound: #GnmRange
1356 * @offsets: double[4]
1357 * @direction: #GODrawingAnchorDir
1358 * @mode: #GnmSOAnchorMode
1360 * A utility routine to initialize an anchor. Useful in case we change fields
1361 * in the future and want to ensure that everything is initialized.
1364 sheet_object_anchor_init (SheetObjectAnchor
*anchor
,
1365 GnmRange
const *r
, const double *offsets
,
1366 GODrawingAnchorDir direction
,
1367 GnmSOAnchorMode mode
)
1372 static GnmRange
const defaultVal
= { { 0, 0 }, { 1, 1 } };
1375 anchor
->cell_bound
= *r
;
1377 if (offsets
== NULL
) {
1378 static double const defaultVal
[4] = { 0., 0., 0., 0. };
1379 offsets
= defaultVal
;
1381 for (i
= 4; i
-- > 0 ; )
1382 anchor
->offset
[i
] = offsets
[i
];
1384 anchor
->base
.direction
= direction
;
1385 anchor
->mode
= mode
;
1386 /* TODO : add sanity checking to handle offsets past edges of col/row */
1389 /*****************************************************************************/
1392 * sheet_object_get_stacking:
1395 * Returns @so's position in the stack of sheet objects.
1398 sheet_object_get_stacking (SheetObject
*so
)
1400 g_return_val_if_fail (so
!= NULL
, 0);
1401 g_return_val_if_fail (so
->sheet
!= NULL
, 0);
1403 return g_slist_index (so
->sheet
->sheet_objects
, so
);
1407 sheet_object_adjust_stacking (SheetObject
*so
, gint offset
)
1410 GSList
**ptr
, *node
= NULL
;
1411 int i
, target
, cur
= 0;
1413 g_return_val_if_fail (so
!= NULL
, 0);
1414 g_return_val_if_fail (so
->sheet
!= NULL
, 0);
1416 for (ptr
= &so
->sheet
->sheet_objects
; *ptr
; ptr
= &(*ptr
)->next
, cur
++)
1417 if ((*ptr
)->data
== so
) {
1419 *ptr
= (*ptr
)->next
;
1423 g_return_val_if_fail (node
!= NULL
, 0);
1425 /* Start at the begining when moving things towards the front */
1427 ptr
= &so
->sheet
->sheet_objects
;
1432 for (target
= cur
- offset
; *ptr
&& i
< target
; ptr
= &(*ptr
)->next
)
1438 /* TODO : Move this to the container */
1439 for (l
= so
->realized_list
; l
; l
= l
->next
) {
1440 GocItem
*item
= GOC_ITEM (l
->data
);
1442 goc_item_raise (item
, offset
);
1444 goc_item_lower (item
, - offset
);
1450 sheet_object_set_anchor_mode (SheetObject
*so
, GnmSOAnchorMode
const *mode
)
1452 /* FIXME: adjust offsets according to the old and new modes */
1455 if (so
->anchor
.mode
== *mode
)
1457 sheet_object_anchor_to_pts (&so
->anchor
, so
->sheet
, pts
);
1458 so
->anchor
.mode
= *mode
;
1459 sheet_object_pts_to_anchor (&so
->anchor
, so
->sheet
, pts
);
1462 /*****************************************************************************/
1464 static GObjectClass
*view_parent_class
;
1467 sheet_object_view_set_bounds (SheetObjectView
*sov
,
1468 double const *coords
, gboolean visible
)
1470 SheetObjectViewClass
*klass
;
1472 g_return_if_fail (GNM_IS_SO_VIEW (sov
));
1473 klass
= GNM_SO_VIEW_GET_CLASS (sov
);
1474 if (NULL
!= klass
->set_bounds
)
1475 (klass
->set_bounds
) (sov
, coords
, visible
);
1479 * sheet_object_view_get_so:
1480 * @sov: #SheetObjectView
1482 * Returns: (transfer none): the #SheetObject owning @view.
1485 sheet_object_view_get_so (SheetObjectView
*view
)
1487 return g_object_get_qdata (G_OBJECT (view
), sov_so_quark
);
1491 sheet_object_view_enter_notify (GocItem
*item
, double x
, double y
)
1495 if (GNM_IS_PANE (item
->canvas
) && scg_wbcg (GNM_SIMPLE_CANVAS (item
->canvas
)->scg
)->new_object
) {
1496 GnmItemGrid
*grid
= GNM_PANE (item
->canvas
)->grid
;
1497 return GOC_ITEM_GET_CLASS (grid
)->enter_notify (GOC_ITEM (grid
), x
, y
);
1500 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1501 gnm_widget_set_cursor_type (GTK_WIDGET (item
->canvas
),
1502 (so
->flags
& SHEET_OBJECT_CAN_PRESS
) ? GDK_HAND2
: GDK_ARROW
);
1507 cb_so_menu_activate (GObject
*menu
, GocItem
*view
)
1509 SheetObjectAction
const *a
= g_object_get_data (menu
, "action");
1512 SheetObject
*so
= sheet_object_view_get_so (GNM_SO_VIEW (view
));
1513 gpointer data
= g_object_get_data (G_OBJECT (view
->canvas
), "sheet-control");
1516 data
= GNM_SIMPLE_CANVAS (view
->canvas
)->scg
;
1518 (a
->func
) (so
, GNM_SC (data
));
1523 cb_ptr_array_free (GPtrArray
*actions
)
1525 g_ptr_array_free (actions
, TRUE
);
1529 * sheet_object_build_menu:
1530 * @view: #SheetObjectView
1531 * @actions: (element-type SheetObjectAction): array of actions.
1532 * @i: index of first action to add in the array.
1534 * Builds the contextual menu for @view.
1535 * Returns: (transfer full): the newly constructed #GtkMenu.
1538 sheet_object_build_menu (SheetObjectView
*view
,
1539 GPtrArray
const *actions
, unsigned *i
)
1541 SheetObjectAction
const *a
;
1542 GtkWidget
*item
, *menu
= gtk_menu_new ();
1544 while (*i
< actions
->len
) {
1545 a
= g_ptr_array_index (actions
, *i
);
1549 if (a
->icon
!= NULL
) {
1550 if (a
->label
!= NULL
) {
1551 item
= gtk_image_menu_item_new_with_mnemonic (_(a
->label
));
1552 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item
),
1553 gtk_image_new_from_icon_name (a
->icon
, GTK_ICON_SIZE_MENU
));
1555 item
= gtk_image_menu_item_new_from_stock (a
->icon
, NULL
);
1556 } else if (a
->label
!= NULL
)
1557 item
= gtk_menu_item_new_with_mnemonic (_(a
->label
));
1559 item
= gtk_separator_menu_item_new ();
1561 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item
),
1562 sheet_object_build_menu (view
, actions
, i
));
1563 else if (a
->label
!= NULL
|| a
->icon
!= NULL
) { /* not a separator or menu */
1564 g_object_set_data (G_OBJECT (item
), "action", (gpointer
)a
);
1565 g_signal_connect_object (G_OBJECT (item
), "activate",
1566 G_CALLBACK (cb_so_menu_activate
), view
, 0);
1567 gtk_widget_set_sensitive (item
, a
->enable_func
== NULL
1568 || a
->enable_func (sheet_object_view_get_so (view
)));
1570 gtk_menu_shell_append (GTK_MENU_SHELL (menu
), item
);
1576 sheet_object_view_button_pressed (GocItem
*item
, int button
, double x
, double y
)
1580 if (GNM_IS_PANE (item
->canvas
)) {
1581 if (scg_wbcg (GNM_SIMPLE_CANVAS (item
->canvas
)->scg
)->new_object
) {
1582 GnmItemGrid
*grid
= GNM_PANE (item
->canvas
)->grid
;
1583 return GOC_ITEM_GET_CLASS (grid
)->button_pressed (GOC_ITEM (grid
), button
, x
, y
);
1589 pane
= GNM_PANE (item
->canvas
);
1590 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1592 x
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
1593 y
*= goc_canvas_get_pixels_per_unit (item
->canvas
);
1594 /* cb_sheet_object_widget_canvas_event calls even if selected */
1595 if (NULL
== g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
)) {
1596 SheetObjectClass
*soc
=
1597 G_TYPE_INSTANCE_GET_CLASS (so
, GNM_SO_TYPE
, SheetObjectClass
);
1598 GdkEventButton
*event
= (GdkEventButton
*) goc_canvas_get_cur_event (item
->canvas
);
1600 if (soc
->interactive
&& button
!= 3)
1603 if (!(event
->state
& GDK_SHIFT_MASK
))
1604 scg_object_unselect (pane
->simple
.scg
, NULL
);
1605 scg_object_select (pane
->simple
.scg
, so
);
1606 if (NULL
== g_hash_table_lookup (pane
->drag
.ctrl_pts
, so
))
1607 return FALSE
; /* protected ? */
1611 gnm_pane_object_start_resize (pane
, button
, x
, y
, so
, 8, FALSE
);
1613 gnm_pane_display_object_menu (pane
, so
, goc_canvas_get_cur_event (item
->canvas
));
1616 GPtrArray
*actions
= g_ptr_array_new ();
1620 so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1621 sheet_object_populate_menu (so
, actions
);
1623 if (actions
->len
== 0) {
1624 g_ptr_array_free (actions
, TRUE
);
1628 menu
= sheet_object_build_menu
1629 (sheet_object_get_view (so
, (SheetObjectViewContainer
*) item
->canvas
),
1631 g_object_set_data_full (G_OBJECT (menu
), "actions", actions
,
1632 (GDestroyNotify
) cb_ptr_array_free
);
1633 gtk_widget_show_all (menu
);
1634 gnumeric_popup_menu (GTK_MENU (menu
),
1635 goc_canvas_get_cur_event (item
->canvas
));
1642 sheet_object_view_button2_pressed (GocItem
*item
, int button
, double x
, double y
)
1644 if (button
== 1 && !GNM_IS_PANE (item
->canvas
)) {
1645 SheetControl
*sc
= GNM_SC (g_object_get_data (G_OBJECT (item
->canvas
), "sheet-control"));
1646 SheetObject
*so
= (SheetObject
*) g_object_get_qdata (G_OBJECT (item
), sov_so_quark
);
1648 if (sc
&& sheet_object_can_edit (so
))
1649 sheet_object_get_editor (so
, sc
);
1655 sheet_object_view_finalize (GObject
*obj
)
1657 SheetObject
*so
= (SheetObject
*) g_object_get_qdata (obj
, sov_so_quark
);
1659 so
->realized_list
= g_list_remove (so
->realized_list
, obj
);
1660 view_parent_class
->finalize (obj
);
1664 sheet_object_view_class_init (GocItemClass
*item_klass
)
1666 GObjectClass
*obj_klass
= (GObjectClass
*) item_klass
;
1667 view_parent_class
= g_type_class_peek_parent (item_klass
);
1669 obj_klass
->finalize
= sheet_object_view_finalize
;
1671 item_klass
->enter_notify
= sheet_object_view_enter_notify
;
1672 item_klass
->button_pressed
= sheet_object_view_button_pressed
;
1673 item_klass
->button2_pressed
= sheet_object_view_button2_pressed
;
1676 GSF_CLASS (SheetObjectView
, sheet_object_view
,
1677 sheet_object_view_class_init
, NULL
,
1680 /*****************************************************************************/
1683 sheet_object_imageable_get_type (void)
1685 static GType type
= 0;
1688 static GTypeInfo
const type_info
= {
1689 sizeof (SheetObjectImageableIface
), /* class_size */
1690 NULL
, /* base_init */
1691 NULL
, /* base_finalize */
1692 NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
1695 type
= g_type_register_static (G_TYPE_INTERFACE
,
1696 "SheetObjectImageable", &type_info
, 0);
1702 #define GNM_SO_IMAGEABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GNM_SO_IMAGEABLE_TYPE, SheetObjectImageableIface))
1705 sheet_object_get_target_list (SheetObject
const *so
)
1707 if (!GNM_IS_SO_IMAGEABLE (so
))
1710 return GNM_SO_IMAGEABLE_CLASS (so
)->get_target_list (so
);
1714 sheet_object_write_image (SheetObject
const *so
, char const *format
, double resolution
,
1715 GsfOutput
*output
, GError
**err
)
1717 g_return_if_fail (GNM_IS_SO_IMAGEABLE (so
));
1719 GNM_SO_IMAGEABLE_CLASS (so
)->write_image (so
, format
, resolution
,
1724 /*****************************************************************************/
1727 sheet_object_exportable_get_type (void)
1729 static GType type
= 0;
1732 static GTypeInfo
const type_info
= {
1733 sizeof (SheetObjectExportableIface
), /* class_size */
1734 NULL
, /* base_init */
1735 NULL
, /* base_finalize */
1736 NULL
, NULL
, NULL
, 0, 0, NULL
, NULL
1739 type
= g_type_register_static (G_TYPE_INTERFACE
,
1740 "SheetObjectExportable", &type_info
, 0);
1746 #define GNM_SO_EXPORTABLE_CLASS(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GNM_SO_EXPORTABLE_TYPE, SheetObjectExportableIface))
1749 sheet_object_exportable_get_target_list (SheetObject
const *so
)
1751 if (!GNM_IS_SO_EXPORTABLE (so
))
1754 return GNM_SO_EXPORTABLE_CLASS (so
)->get_target_list (so
);
1758 sheet_object_write_object (SheetObject
const *so
, char const *format
,
1759 GsfOutput
*output
, GError
**err
,
1760 GnmConventions
const *convs
)
1764 g_return_if_fail (GNM_IS_SO_EXPORTABLE (so
));
1766 locale
= gnm_push_C_locale ();
1767 GNM_SO_EXPORTABLE_CLASS (so
)->
1768 write_object (so
, format
, output
, err
, convs
);
1769 gnm_pop_C_locale (locale
);
1773 * sheet_object_move_undo:
1774 * @objects: (element-type SheetObject):
1777 * Returns: (transfer full): the newly allocated #GOUndo.
1780 sheet_object_move_undo (GSList
*objects
, gboolean objects_created
)
1782 GOUndo
*undo
= NULL
;
1783 GSList
*objs
= objects
;
1785 g_return_val_if_fail (NULL
!= objects
, NULL
);
1787 for (; objs
; objs
= objs
->next
) {
1788 SheetObject
*obj
= objs
->data
;
1789 SheetObjectAnchor
*tmp
;
1791 if (objects_created
) {
1792 undo
= go_undo_combine
1795 (g_object_ref (obj
),
1796 (GOUndoUnaryFunc
) sheet_object_clear_sheet
,
1797 (GFreeFunc
) g_object_unref
));
1800 tmp
= g_new (SheetObjectAnchor
, 1);
1801 *tmp
= *sheet_object_get_anchor (obj
);
1802 undo
= go_undo_combine
1803 (undo
, go_undo_binary_new
1804 (g_object_ref (obj
), tmp
,
1805 (GOUndoBinaryFunc
) sheet_object_set_anchor
,
1806 (GFreeFunc
) g_object_unref
,
1807 (GFreeFunc
) g_free
));
1813 * sheet_object_move_do:
1814 * @objects: (element-type SheetObject):
1815 * @anchors: (element-type SheetObjectAnchor):
1818 * Returns: (transfer full): the newly allocated #GOUndo.
1821 sheet_object_move_do (GSList
*objects
, GSList
*anchors
,
1822 gboolean objects_created
)
1824 GOUndo
*undo
= NULL
;
1825 GSList
*objs
= objects
, *anchs
= anchors
;
1827 g_return_val_if_fail (NULL
!= objects
, NULL
);
1828 g_return_val_if_fail (NULL
!= anchors
, NULL
);
1829 g_return_val_if_fail (g_slist_length (objects
)
1830 == g_slist_length (anchors
), NULL
);
1832 for (; objs
&& anchs
; objs
= objs
->next
, anchs
= anchs
->next
) {
1833 SheetObject
*obj
= objs
->data
;
1834 SheetObjectAnchor
*anch
= anchs
->data
;
1835 SheetObjectAnchor
*tmp
;
1837 if (objects_created
) {
1838 undo
= go_undo_combine
1841 (g_object_ref (obj
),
1842 sheet_object_get_sheet (obj
),
1843 (GOUndoBinaryFunc
) sheet_object_set_sheet
,
1844 (GFreeFunc
) g_object_unref
,
1847 tmp
= g_new (SheetObjectAnchor
, 1);
1849 undo
= go_undo_combine
1851 (g_object_ref (obj
), tmp
,
1852 (GOUndoBinaryFunc
) sheet_object_set_anchor
,
1853 (GFreeFunc
) g_object_unref
,
1854 (GFreeFunc
) g_free
), undo
);
1860 /*****************************************************************************/
1863 * sheet_objects_init: (skip)
1866 sheet_objects_init (void)
1872 GNM_GO_DATA_SCALAR_TYPE
;
1873 GNM_GO_DATA_VECTOR_TYPE
;
1874 GNM_GO_DATA_MATRIX_TYPE
;
1875 GNM_CELL_COMMENT_TYPE
;
1877 sheet_object_widget_register ();
1878 sov_so_quark
= g_quark_from_static_string ("SheetObject");
1879 sov_container_quark
= g_quark_from_static_string ("SheetObjectViewContainer");