2 * sheet.c: Implements the sheet management and per-sheet storage
4 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
5 * Copyright (C) 1997-1999 Miguel de Icaza (miguel@kernel.org)
6 * Copyright (C) 1999-2009 Morten Welinder (terra@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
23 #include <gnumeric-config.h>
27 #include <sheet-view.h>
28 #include <command-context.h>
29 #include <sheet-control.h>
30 #include <sheet-style.h>
31 #include <workbook-priv.h>
32 #include <workbook-control.h>
33 #include <workbook-view.h>
34 #include <parse-util.h>
35 #include <dependent.h>
37 #include <number-match.h>
38 #include <clipboard.h>
39 #include <selection.h>
41 #include <print-info.h>
43 #include <style-color.h>
44 #include <style-font.h>
45 #include <application.h>
49 #include <sheet-merge.h>
50 #include <sheet-private.h>
51 #include <expr-name.h>
53 #include <rendered-value.h>
54 #include <gnumeric-conf.h>
55 #include <sheet-object-impl.h>
56 #include <sheet-object-cell-comment.h>
57 #include <tools/gnm-solver.h>
59 #include <sheet-filter.h>
60 #include <sheet-filter-combo.h>
61 #include <gnm-sheet-slicer.h>
62 #include <tools/scenarios.h>
63 #include <cell-draw.h>
66 #include <goffice/goffice.h>
69 #include <gsf/gsf-impl-utils.h>
74 gnm_sheet_size_copy (GnmSheetSize
*size
)
76 GnmSheetSize
*res
= g_new (GnmSheetSize
, 1);
82 gnm_sheet_size_get_type (void)
87 t
= g_boxed_type_register_static ("GnmSheetSize",
88 (GBoxedCopyFunc
)gnm_sheet_size_copy
,
89 (GBoxedFreeFunc
)g_free
);
95 DETACHED_FROM_WORKBOOK
,
99 static guint signals
[LAST_SIGNAL
] = { 0 };
104 void (*detached_from_workbook
) (Sheet
*, Workbook
*wb
);
106 typedef Sheet GnmSheet
;
115 PROP_DISPLAY_FORMULAS
,
118 PROP_DISPLAY_COLUMN_HEADER
,
119 PROP_DISPLAY_ROW_HEADER
,
120 PROP_DISPLAY_OUTLINES
,
121 PROP_DISPLAY_OUTLINES_BELOW
,
122 PROP_DISPLAY_OUTLINES_RIGHT
,
125 PROP_PROTECTED_ALLOW_EDIT_OBJECTS
,
126 PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
,
127 PROP_PROTECTED_ALLOW_CELL_FORMATTING
,
128 PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
,
129 PROP_PROTECTED_ALLOW_ROW_FORMATTING
,
130 PROP_PROTECTED_ALLOW_INSERT_COLUMNS
,
131 PROP_PROTECTED_ALLOW_INSERT_ROWS
,
132 PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
,
133 PROP_PROTECTED_ALLOW_DELETE_COLUMNS
,
134 PROP_PROTECTED_ALLOW_DELETE_ROWS
,
135 PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
,
136 PROP_PROTECTED_ALLOW_SORT_RANGES
,
137 PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
,
138 PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
,
139 PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
,
152 static void gnm_sheet_finalize (GObject
*obj
);
154 static GObjectClass
*parent_class
;
157 col_row_collection_resize (ColRowCollection
*infos
, int size
)
159 int end_idx
= COLROW_SEGMENT_INDEX (size
);
160 int i
= infos
->info
->len
- 1;
162 while (i
>= end_idx
) {
163 ColRowSegment
*segment
= g_ptr_array_index (infos
->info
, i
);
166 g_ptr_array_index (infos
->info
, i
) = NULL
;
171 g_ptr_array_set_size (infos
->info
, end_idx
);
175 sheet_set_direction (Sheet
*sheet
, gboolean text_is_rtl
)
179 text_is_rtl
= !!text_is_rtl
;
180 if (text_is_rtl
== sheet
->text_is_rtl
)
183 sheet_mark_dirty (sheet
);
185 sheet
->text_is_rtl
= text_is_rtl
;
186 sheet
->priv
->reposition_objects
.col
= 0;
187 sheet_range_calc_spans (sheet
,
188 range_init_full_sheet (&r
, sheet
),
189 GNM_SPANCALC_RE_RENDER
);
193 sheet_set_visibility (Sheet
*sheet
, GnmSheetVisibility visibility
)
195 if (sheet
->visibility
== visibility
)
198 sheet
->visibility
= visibility
;
199 sheet_mark_dirty (sheet
);
203 cb_re_render_formulas (G_GNUC_UNUSED gpointer unused
,
205 G_GNUC_UNUSED gpointer user
)
207 if (gnm_cell_has_expr (cell
)) {
208 gnm_cell_unrender (cell
);
209 sheet_cell_queue_respan (cell
);
214 re_render_formulas (Sheet
const *sheet
)
216 sheet_cell_foreach (sheet
, (GHFunc
)cb_re_render_formulas
, NULL
);
220 sheet_set_conventions (Sheet
*sheet
, GnmConventions
const *convs
)
222 if (sheet
->convs
== convs
)
224 gnm_conventions_unref (sheet
->convs
);
225 sheet
->convs
= gnm_conventions_ref (convs
);
226 if (sheet
->display_formulas
)
227 re_render_formulas (sheet
);
228 SHEET_FOREACH_VIEW (sheet
, sv
,
229 sv
->edit_pos_changed
.content
= TRUE
;);
230 sheet_mark_dirty (sheet
);
233 GnmConventions
const *
234 sheet_get_conventions (Sheet
const *sheet
)
236 g_return_val_if_fail (IS_SHEET (sheet
), gnm_conventions_default
);
242 cb_sheet_set_hide_zeros (G_GNUC_UNUSED gpointer unused
,
244 G_GNUC_UNUSED gpointer user
)
246 if (gnm_cell_is_zero (cell
))
247 gnm_cell_unrender (cell
);
251 sheet_set_hide_zeros (Sheet
*sheet
, gboolean hide
)
254 if (sheet
->hide_zero
== hide
)
257 sheet
->hide_zero
= hide
;
258 sheet_mark_dirty (sheet
);
260 sheet_cell_foreach (sheet
, (GHFunc
)cb_sheet_set_hide_zeros
, NULL
);
264 sheet_set_name (Sheet
*sheet
, char const *new_name
)
266 Workbook
*wb
= sheet
->workbook
;
269 char *new_name_unquoted
;
271 g_return_if_fail (new_name
!= NULL
);
273 /* No change whatsoever. */
274 if (go_str_compare (sheet
->name_unquoted
, new_name
) == 0)
277 /* Mark the sheet dirty unless this is the initial name. */
278 if (sheet
->name_unquoted
)
279 sheet_mark_dirty (sheet
);
281 sucker
= wb
? workbook_sheet_by_name (wb
, new_name
) : NULL
;
282 if (sucker
&& sucker
!= sheet
) {
284 * Prevent a name clash. With this you can swap names by
285 * setting just the two names.
287 char *sucker_name
= workbook_sheet_get_free_name (wb
, new_name
, TRUE
, FALSE
);
289 g_warning ("Renaming %s to %s to avoid clash.\n", sucker
->name_unquoted
, sucker_name
);
291 g_object_set (sucker
, "name", sucker_name
, NULL
);
292 g_free (sucker_name
);
295 attached
= wb
!= NULL
&&
296 sheet
->index_in_wb
!= -1 &&
297 sheet
->name_case_insensitive
;
299 /* FIXME: maybe have workbook_sheet_detach_internal for this. */
301 g_hash_table_remove (wb
->sheet_hash_private
,
302 sheet
->name_case_insensitive
);
304 /* Copy before free. */
305 new_name_unquoted
= g_strdup (new_name
);
307 g_free (sheet
->name_unquoted
);
308 g_free (sheet
->name_quoted
);
309 g_free (sheet
->name_unquoted_collate_key
);
310 g_free (sheet
->name_case_insensitive
);
311 sheet
->name_unquoted
= new_name_unquoted
;
312 sheet
->name_quoted
= g_string_free
313 (gnm_expr_conv_quote (sheet
->convs
, new_name_unquoted
),
315 sheet
->name_unquoted_collate_key
=
316 g_utf8_collate_key (new_name_unquoted
, -1);
317 sheet
->name_case_insensitive
=
318 g_utf8_casefold (new_name_unquoted
, -1);
320 /* FIXME: maybe have workbook_sheet_attach_internal for this. */
322 g_hash_table_insert (wb
->sheet_hash_private
,
323 sheet
->name_case_insensitive
,
326 if (!sheet
->being_constructed
&&
327 sheet
->sheet_type
== GNM_SHEET_DATA
) {
328 /* We have to fix the Sheet_Title name */
332 parse_pos_init_sheet (&pp
, sheet
);
333 nexpr
= expr_name_lookup (&pp
, "Sheet_Title");
335 GnmExprTop
const *texpr
=
336 gnm_expr_top_new_constant
337 (value_new_string (sheet
->name_unquoted
));
338 expr_name_set_expr (nexpr
, texpr
);
343 struct resize_colrow
{
350 cb_colrow_compute_pixels_from_pts (GnmColRowIter
const *iter
,
353 struct resize_colrow
*data
= data_
;
354 colrow_compute_pixels_from_pts ((ColRowInfo
*)iter
->cri
,
355 data
->sheet
, data
->is_cols
,
361 cb_clear_rendered_cells (G_GNUC_UNUSED gpointer ignored
, GnmCell
*cell
)
363 if (gnm_cell_get_rendered_value (cell
) != NULL
) {
364 sheet_cell_queue_respan (cell
);
365 gnm_cell_unrender (cell
);
370 sheet_scale_changed (Sheet
*sheet
, gboolean cols_rescaled
, gboolean rows_rescaled
)
372 g_return_if_fail (cols_rescaled
|| rows_rescaled
);
374 /* Then every column and row */
376 struct resize_colrow closure
;
378 closure
.sheet
= sheet
;
379 closure
.is_cols
= TRUE
;
380 closure
.scale
= colrow_compute_pixel_scale (sheet
, TRUE
);
382 colrow_compute_pixels_from_pts (&sheet
->cols
.default_style
,
383 sheet
, TRUE
, closure
.scale
);
384 sheet_colrow_foreach (sheet
, TRUE
, 0, -1,
385 cb_colrow_compute_pixels_from_pts
,
389 struct resize_colrow closure
;
391 closure
.sheet
= sheet
;
392 closure
.is_cols
= FALSE
;
393 closure
.scale
= colrow_compute_pixel_scale (sheet
, FALSE
);
395 colrow_compute_pixels_from_pts (&sheet
->rows
.default_style
,
396 sheet
, FALSE
, closure
.scale
);
397 sheet_colrow_foreach (sheet
, FALSE
, 0, -1,
398 cb_colrow_compute_pixels_from_pts
,
402 sheet_cell_foreach (sheet
, (GHFunc
)&cb_clear_rendered_cells
, NULL
);
403 SHEET_FOREACH_CONTROL (sheet
, view
, control
, sc_scale_changed (control
););
407 sheet_set_display_formulas (Sheet
*sheet
, gboolean display
)
410 if (sheet
->display_formulas
== display
)
413 sheet
->display_formulas
= display
;
414 sheet_mark_dirty (sheet
);
415 if (!sheet
->being_constructed
)
416 sheet_scale_changed (sheet
, TRUE
, FALSE
);
420 sheet_set_zoom_factor (Sheet
*sheet
, double factor
)
422 if (fabs (factor
- sheet
->last_zoom_factor_used
) < 1e-6)
424 sheet
->last_zoom_factor_used
= factor
;
425 if (!sheet
->being_constructed
)
426 sheet_scale_changed (sheet
, TRUE
, TRUE
);
430 gnm_sheet_set_property (GObject
*object
, guint property_id
,
431 GValue
const *value
, GParamSpec
*pspec
)
433 Sheet
*sheet
= (Sheet
*)object
;
435 switch (property_id
) {
436 case PROP_SHEET_TYPE
:
437 /* Construction-time only */
438 sheet
->sheet_type
= g_value_get_enum (value
);
441 /* Construction-time only */
442 sheet
->workbook
= g_value_get_object (value
);
445 sheet_set_name (sheet
, g_value_get_string (value
));
448 sheet_set_direction (sheet
, g_value_get_boolean (value
));
450 case PROP_VISIBILITY
:
451 sheet_set_visibility (sheet
, g_value_get_enum (value
));
453 case PROP_DISPLAY_FORMULAS
:
454 sheet_set_display_formulas (sheet
, g_value_get_boolean (value
));
456 case PROP_DISPLAY_ZEROS
:
457 sheet_set_hide_zeros (sheet
, !g_value_get_boolean (value
));
459 case PROP_DISPLAY_GRID
:
460 sheet
->hide_grid
= !g_value_get_boolean (value
);
462 case PROP_DISPLAY_COLUMN_HEADER
:
463 sheet
->hide_col_header
= !g_value_get_boolean (value
);
465 case PROP_DISPLAY_ROW_HEADER
:
466 sheet
->hide_row_header
= !g_value_get_boolean (value
);
468 case PROP_DISPLAY_OUTLINES
:
469 sheet
->display_outlines
= !!g_value_get_boolean (value
);
471 case PROP_DISPLAY_OUTLINES_BELOW
:
472 sheet
->outline_symbols_below
= !!g_value_get_boolean (value
);
474 case PROP_DISPLAY_OUTLINES_RIGHT
:
475 sheet
->outline_symbols_right
= !!g_value_get_boolean (value
);
479 sheet
->is_protected
= !!g_value_get_boolean (value
);
481 case PROP_PROTECTED_ALLOW_EDIT_OBJECTS
:
482 sheet
->protected_allow
.edit_objects
= !!g_value_get_boolean (value
);
484 case PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
:
485 sheet
->protected_allow
.edit_scenarios
= !!g_value_get_boolean (value
);
487 case PROP_PROTECTED_ALLOW_CELL_FORMATTING
:
488 sheet
->protected_allow
.cell_formatting
= !!g_value_get_boolean (value
);
490 case PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
:
491 sheet
->protected_allow
.column_formatting
= !!g_value_get_boolean (value
);
493 case PROP_PROTECTED_ALLOW_ROW_FORMATTING
:
494 sheet
->protected_allow
.row_formatting
= !!g_value_get_boolean (value
);
496 case PROP_PROTECTED_ALLOW_INSERT_COLUMNS
:
497 sheet
->protected_allow
.insert_columns
= !!g_value_get_boolean (value
);
499 case PROP_PROTECTED_ALLOW_INSERT_ROWS
:
500 sheet
->protected_allow
.insert_rows
= !!g_value_get_boolean (value
);
502 case PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
:
503 sheet
->protected_allow
.insert_hyperlinks
= !!g_value_get_boolean (value
);
505 case PROP_PROTECTED_ALLOW_DELETE_COLUMNS
:
506 sheet
->protected_allow
.delete_columns
= !!g_value_get_boolean (value
);
508 case PROP_PROTECTED_ALLOW_DELETE_ROWS
:
509 sheet
->protected_allow
.delete_rows
= !!g_value_get_boolean (value
);
511 case PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
:
512 sheet
->protected_allow
.select_locked_cells
= !!g_value_get_boolean (value
);
514 case PROP_PROTECTED_ALLOW_SORT_RANGES
:
515 sheet
->protected_allow
.sort_ranges
= !!g_value_get_boolean (value
);
517 case PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
:
518 sheet
->protected_allow
.edit_auto_filters
= !!g_value_get_boolean (value
);
520 case PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
:
521 sheet
->protected_allow
.edit_pivottable
= !!g_value_get_boolean (value
);
523 case PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
:
524 sheet
->protected_allow
.select_unlocked_cells
= !!g_value_get_boolean (value
);
527 case PROP_CONVENTIONS
:
528 sheet_set_conventions (sheet
, g_value_get_boxed (value
));
530 case PROP_USE_R1C1
: /* convenience api */
531 sheet_set_conventions (sheet
, !!g_value_get_boolean (value
)
532 ? gnm_conventions_xls_r1c1
: gnm_conventions_default
);
535 case PROP_TAB_FOREGROUND
: {
536 GnmColor
*color
= g_value_dup_boxed (value
);
537 style_color_unref (sheet
->tab_text_color
);
538 sheet
->tab_text_color
= color
;
539 sheet_mark_dirty (sheet
);
542 case PROP_TAB_BACKGROUND
: {
543 GnmColor
*color
= g_value_dup_boxed (value
);
544 style_color_unref (sheet
->tab_color
);
545 sheet
->tab_color
= color
;
546 sheet_mark_dirty (sheet
);
549 case PROP_ZOOM_FACTOR
:
550 sheet_set_zoom_factor (sheet
, g_value_get_double (value
));
553 /* Construction-time only */
554 sheet
->size
.max_cols
= g_value_get_int (value
);
557 /* Construction-time only */
558 sheet
->size
.max_rows
= g_value_get_int (value
);
561 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
567 gnm_sheet_get_property (GObject
*object
, guint property_id
,
568 GValue
*value
, GParamSpec
*pspec
)
570 Sheet
*sheet
= (Sheet
*)object
;
572 switch (property_id
) {
573 case PROP_SHEET_TYPE
:
574 g_value_set_enum (value
, sheet
->sheet_type
);
577 g_value_set_object (value
, sheet
->workbook
);
580 g_value_set_string (value
, sheet
->name_unquoted
);
583 g_value_set_boolean (value
, sheet
->text_is_rtl
);
585 case PROP_VISIBILITY
:
586 g_value_set_enum (value
, sheet
->visibility
);
588 case PROP_DISPLAY_FORMULAS
:
589 g_value_set_boolean (value
, sheet
->display_formulas
);
591 case PROP_DISPLAY_ZEROS
:
592 g_value_set_boolean (value
, !sheet
->hide_zero
);
594 case PROP_DISPLAY_GRID
:
595 g_value_set_boolean (value
, !sheet
->hide_grid
);
597 case PROP_DISPLAY_COLUMN_HEADER
:
598 g_value_set_boolean (value
, !sheet
->hide_col_header
);
600 case PROP_DISPLAY_ROW_HEADER
:
601 g_value_set_boolean (value
, !sheet
->hide_row_header
);
603 case PROP_DISPLAY_OUTLINES
:
604 g_value_set_boolean (value
, sheet
->display_outlines
);
606 case PROP_DISPLAY_OUTLINES_BELOW
:
607 g_value_set_boolean (value
, sheet
->outline_symbols_below
);
609 case PROP_DISPLAY_OUTLINES_RIGHT
:
610 g_value_set_boolean (value
, sheet
->outline_symbols_right
);
614 g_value_set_boolean (value
, sheet
->is_protected
);
616 case PROP_PROTECTED_ALLOW_EDIT_OBJECTS
:
617 g_value_set_boolean (value
, sheet
->protected_allow
.edit_objects
);
619 case PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
:
620 g_value_set_boolean (value
, sheet
->protected_allow
.edit_scenarios
);
622 case PROP_PROTECTED_ALLOW_CELL_FORMATTING
:
623 g_value_set_boolean (value
, sheet
->protected_allow
.cell_formatting
);
625 case PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
:
626 g_value_set_boolean (value
, sheet
->protected_allow
.column_formatting
);
628 case PROP_PROTECTED_ALLOW_ROW_FORMATTING
:
629 g_value_set_boolean (value
, sheet
->protected_allow
.row_formatting
);
631 case PROP_PROTECTED_ALLOW_INSERT_COLUMNS
:
632 g_value_set_boolean (value
, sheet
->protected_allow
.insert_columns
);
634 case PROP_PROTECTED_ALLOW_INSERT_ROWS
:
635 g_value_set_boolean (value
, sheet
->protected_allow
.insert_rows
);
637 case PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
:
638 g_value_set_boolean (value
, sheet
->protected_allow
.insert_hyperlinks
);
640 case PROP_PROTECTED_ALLOW_DELETE_COLUMNS
:
641 g_value_set_boolean (value
, sheet
->protected_allow
.delete_columns
);
643 case PROP_PROTECTED_ALLOW_DELETE_ROWS
:
644 g_value_set_boolean (value
, sheet
->protected_allow
.delete_rows
);
646 case PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
:
647 g_value_set_boolean (value
, sheet
->protected_allow
.select_locked_cells
);
649 case PROP_PROTECTED_ALLOW_SORT_RANGES
:
650 g_value_set_boolean (value
, sheet
->protected_allow
.sort_ranges
);
652 case PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
:
653 g_value_set_boolean (value
, sheet
->protected_allow
.edit_auto_filters
);
655 case PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
:
656 g_value_set_boolean (value
, sheet
->protected_allow
.edit_pivottable
);
658 case PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
:
659 g_value_set_boolean (value
, sheet
->protected_allow
.select_unlocked_cells
);
662 case PROP_CONVENTIONS
:
663 g_value_set_boxed (value
, sheet
->convs
);
665 case PROP_USE_R1C1
: /* convenience api */
666 g_value_set_boolean (value
, sheet
->convs
->r1c1_addresses
);
669 case PROP_TAB_FOREGROUND
:
670 g_value_set_boxed (value
, sheet
->tab_text_color
);
672 case PROP_TAB_BACKGROUND
:
673 g_value_set_boxed (value
, sheet
->tab_color
);
675 case PROP_ZOOM_FACTOR
:
676 g_value_set_double (value
, sheet
->last_zoom_factor_used
);
679 g_value_set_int (value
, sheet
->size
.max_cols
);
682 g_value_set_int (value
, sheet
->size
.max_rows
);
685 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
691 gnm_sheet_constructed (GObject
*obj
)
693 Sheet
*sheet
= SHEET (obj
);
695 if (parent_class
->constructed
)
696 parent_class
->constructed (obj
);
698 /* Now sheet_type, max_cols, and max_rows have been set. */
699 sheet
->being_constructed
= FALSE
;
701 col_row_collection_resize (&sheet
->cols
, sheet
->size
.max_cols
);
702 col_row_collection_resize (&sheet
->rows
, sheet
->size
.max_rows
);
704 sheet
->priv
->reposition_objects
.col
= sheet
->size
.max_cols
;
705 sheet
->priv
->reposition_objects
.row
= sheet
->size
.max_rows
;
707 range_init_full_sheet (&sheet
->priv
->unhidden_region
, sheet
);
708 sheet_style_init (sheet
);
710 sheet
->deps
= gnm_dep_container_new (sheet
);
712 switch (sheet
->sheet_type
) {
714 sheet
->display_formulas
= TRUE
;
716 case GNM_SHEET_OBJECT
:
717 sheet
->hide_grid
= TRUE
;
718 sheet
->hide_col_header
= sheet
->hide_row_header
= TRUE
;
719 colrow_compute_pixels_from_pts (&sheet
->rows
.default_style
,
721 colrow_compute_pixels_from_pts (&sheet
->cols
.default_style
,
724 case GNM_SHEET_DATA
: {
725 /* We have to add permanent names */
726 GnmExprTop
const *texpr
;
728 if (sheet
->name_unquoted
)
729 texpr
= gnm_expr_top_new_constant
730 (value_new_string (sheet
->name_unquoted
));
732 texpr
= gnm_expr_top_new_constant
733 (value_new_error_REF (NULL
));
734 expr_name_perm_add (sheet
, "Sheet_Title",
737 texpr
= gnm_expr_top_new_constant
738 (value_new_error_REF (NULL
));
739 expr_name_perm_add (sheet
, "Print_Area",
744 g_assert_not_reached ();
747 sheet_scale_changed (sheet
, TRUE
, TRUE
);
751 cell_set_hash (GnmCell
const *key
)
753 guint32 r
= key
->pos
.row
;
754 guint32 c
= key
->pos
.col
;
758 h
*= (guint32
)123456789;
760 h
*= (guint32
)123456789;
766 cell_set_equal (GnmCell
const *a
, GnmCell
const *b
)
768 return (a
->pos
.row
== b
->pos
.row
&& a
->pos
.col
== b
->pos
.col
);
772 gnm_sheet_init (Sheet
*sheet
)
774 PangoContext
*context
;
776 sheet
->priv
= g_new0 (SheetPrivate
, 1);
777 sheet
->being_constructed
= TRUE
;
779 sheet
->sheet_views
= g_ptr_array_new ();
781 /* Init, focus, and load handle setting these if/when necessary */
782 sheet
->priv
->recompute_visibility
= TRUE
;
783 sheet
->priv
->recompute_spans
= TRUE
;
785 sheet
->is_protected
= FALSE
;
786 sheet
->protected_allow
.edit_scenarios
= FALSE
;
787 sheet
->protected_allow
.cell_formatting
= FALSE
;
788 sheet
->protected_allow
.column_formatting
= FALSE
;
789 sheet
->protected_allow
.row_formatting
= FALSE
;
790 sheet
->protected_allow
.insert_columns
= FALSE
;
791 sheet
->protected_allow
.insert_rows
= FALSE
;
792 sheet
->protected_allow
.insert_hyperlinks
= FALSE
;
793 sheet
->protected_allow
.delete_columns
= FALSE
;
794 sheet
->protected_allow
.delete_rows
= FALSE
;
795 sheet
->protected_allow
.select_locked_cells
= TRUE
;
796 sheet
->protected_allow
.sort_ranges
= FALSE
;
797 sheet
->protected_allow
.edit_auto_filters
= FALSE
;
798 sheet
->protected_allow
.edit_pivottable
= FALSE
;
799 sheet
->protected_allow
.select_unlocked_cells
= TRUE
;
801 sheet
->hide_zero
= FALSE
;
802 sheet
->display_outlines
= TRUE
;
803 sheet
->outline_symbols_below
= TRUE
;
804 sheet
->outline_symbols_right
= TRUE
;
805 sheet
->tab_color
= NULL
;
806 sheet
->tab_text_color
= NULL
;
807 sheet
->visibility
= GNM_SHEET_VISIBILITY_VISIBLE
;
809 sheet
->text_is_rtl
= (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL
);
811 sheet
->text_is_rtl
= FALSE
;
814 sheet
->sheet_objects
= NULL
;
815 sheet
->max_object_extent
.col
= sheet
->max_object_extent
.row
= 0;
817 sheet
->solver_parameters
= gnm_solver_param_new (sheet
);
819 sheet
->cols
.max_used
= -1;
820 sheet
->cols
.info
= g_ptr_array_new ();
821 sheet_col_set_default_size_pts (sheet
, 48);
823 sheet
->rows
.max_used
= -1;
824 sheet
->rows
.info
= g_ptr_array_new ();
825 sheet_row_set_default_size_pts (sheet
, 12.75);
827 sheet
->print_info
= gnm_print_information_new (FALSE
);
829 sheet
->filters
= NULL
;
830 sheet
->scenarios
= NULL
;
831 sheet
->sort_setups
= NULL
;
832 sheet
->list_merged
= NULL
;
833 sheet
->hash_merged
= g_hash_table_new ((GHashFunc
)&gnm_cellpos_hash
,
834 (GCompareFunc
)&gnm_cellpos_equal
);
836 sheet
->cell_hash
= g_hash_table_new ((GHashFunc
)&cell_set_hash
,
837 (GCompareFunc
)&cell_set_equal
);
839 /* Init preferences */
840 sheet
->convs
= gnm_conventions_ref (gnm_conventions_default
);
842 /* FIXME: probably not here. */
843 /* See also gtk_widget_create_pango_context (). */
844 sheet
->last_zoom_factor_used
= -1; /* Overridden later */
845 context
= gnm_pango_context_get ();
846 sheet
->rendered_values
= gnm_rvc_new (context
, 5000);
847 g_object_unref (context
);
849 /* Init menu states */
850 sheet
->priv
->enable_showhide_detail
= TRUE
;
852 sheet
->names
= gnm_named_expr_collection_new ();
853 sheet
->style_data
= NULL
;
855 sheet
->index_in_wb
= -1;
858 static Sheet the_invalid_sheet
;
859 Sheet
*invalid_sheet
= &the_invalid_sheet
;
862 gnm_sheet_class_init (GObjectClass
*gobject_class
)
864 if (GNM_MAX_COLS
> 364238) {
866 g_warning (_("This is a special version of Gnumeric. It has been compiled\n"
867 "with support for a very large number of columns. Access to the\n"
868 "column named TRUE may conflict with the constant of the same\n"
869 "name. Expect weirdness."));
872 parent_class
= g_type_class_peek_parent (gobject_class
);
874 gobject_class
->set_property
= gnm_sheet_set_property
;
875 gobject_class
->get_property
= gnm_sheet_get_property
;
876 gobject_class
->finalize
= gnm_sheet_finalize
;
877 gobject_class
->constructed
= gnm_sheet_constructed
;
879 g_object_class_install_property (gobject_class
, PROP_SHEET_TYPE
,
880 g_param_spec_enum ("sheet-type",
882 P_("Which type of sheet this is."),
887 G_PARAM_CONSTRUCT_ONLY
));
888 g_object_class_install_property (gobject_class
, PROP_WORKBOOK
,
889 g_param_spec_object ("workbook",
890 P_("Parent workbook"),
891 P_("The workbook in which this sheet lives"),
895 G_PARAM_CONSTRUCT_ONLY
));
896 g_object_class_install_property (gobject_class
, PROP_NAME
,
897 g_param_spec_string ("name",
899 P_("The name of the sheet."),
903 g_object_class_install_property (gobject_class
, PROP_RTL
,
904 g_param_spec_boolean ("text-is-rtl",
906 P_("Text goes from right to left."),
910 g_object_class_install_property (gobject_class
, PROP_VISIBILITY
,
911 g_param_spec_enum ("visibility",
913 P_("How visible the sheet is."),
914 GNM_SHEET_VISIBILITY_TYPE
,
915 GNM_SHEET_VISIBILITY_VISIBLE
,
918 g_object_class_install_property (gobject_class
, PROP_DISPLAY_FORMULAS
,
919 g_param_spec_boolean ("display-formulas",
920 P_("Display Formul\303\246"),
921 P_("Control whether formul\303\246 are shown instead of values."),
925 g_object_class_install_property (gobject_class
, PROP_DISPLAY_ZEROS
,
926 g_param_spec_boolean ("display-zeros", _("Display Zeros"),
927 _("Control whether zeros are shown are blanked out."),
931 g_object_class_install_property (gobject_class
, PROP_DISPLAY_GRID
,
932 g_param_spec_boolean ("display-grid", _("Display Grid"),
933 _("Control whether the grid is shown."),
937 g_object_class_install_property (gobject_class
, PROP_DISPLAY_COLUMN_HEADER
,
938 g_param_spec_boolean ("display-column-header",
939 P_("Display Column Headers"),
940 P_("Control whether column headers are shown."),
944 g_object_class_install_property (gobject_class
, PROP_DISPLAY_ROW_HEADER
,
945 g_param_spec_boolean ("display-row-header",
946 P_("Display Row Headers"),
947 P_("Control whether row headers are shown."),
951 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES
,
952 g_param_spec_boolean ("display-outlines",
953 P_("Display Outlines"),
954 P_("Control whether outlines are shown."),
958 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES_BELOW
,
959 g_param_spec_boolean ("display-outlines-below",
960 P_("Display Outlines Below"),
961 P_("Control whether outline symbols are shown below."),
965 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES_RIGHT
,
966 g_param_spec_boolean ("display-outlines-right",
967 P_("Display Outlines Right"),
968 P_("Control whether outline symbols are shown to the right."),
973 g_object_class_install_property (gobject_class
, PROP_PROTECTED
,
974 g_param_spec_boolean ("protected",
976 P_("Sheet is protected."),
977 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
978 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_OBJECTS
,
979 g_param_spec_boolean ("protected-allow-edit-objects",
980 P_("Protected Allow Edit objects"),
981 P_("Allow objects to be edited while a sheet is protected"),
982 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
983 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
,
984 g_param_spec_boolean ("protected-allow-edit-scenarios",
985 P_("Protected allow edit scenarios"),
986 P_("Allow scenarios to be edited while a sheet is protected"),
987 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
988 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_CELL_FORMATTING
,
989 g_param_spec_boolean ("protected-allow-cell-formatting",
990 P_("Protected allow cell formatting"),
991 P_("Allow cell format changes while a sheet is protected"),
992 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
993 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
,
994 g_param_spec_boolean ("protected-allow-column-formatting",
995 P_("Protected allow column formatting"),
996 P_("Allow column formatting while a sheet is protected"),
997 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
998 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_ROW_FORMATTING
,
999 g_param_spec_boolean ("protected-allow-row-formatting",
1000 P_("Protected allow row formatting"),
1001 P_("Allow row formatting while a sheet is protected"),
1002 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1003 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_COLUMNS
,
1004 g_param_spec_boolean ("protected-allow-insert-columns",
1005 P_("Protected allow insert columns"),
1006 P_("Allow columns to be inserted while a sheet is protected"),
1007 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1008 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_ROWS
,
1009 g_param_spec_boolean ("protected-allow-insert-rows",
1010 P_("Protected allow insert rows"),
1011 P_("Allow rows to be inserted while a sheet is protected"),
1012 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1013 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
,
1014 g_param_spec_boolean ("protected-allow-insert-hyperlinks",
1015 P_("Protected allow insert hyperlinks"),
1016 P_("Allow hyperlinks to be inserted while a sheet is protected"),
1017 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1018 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_DELETE_COLUMNS
,
1019 g_param_spec_boolean ("protected-allow-delete-columns",
1020 P_("Protected allow delete columns"),
1021 P_("Allow columns to be deleted while a sheet is protected"),
1022 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1023 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_DELETE_ROWS
,
1024 g_param_spec_boolean ("protected-allow-delete-rows",
1025 P_("Protected allow delete rows"),
1026 P_("Allow rows to be deleted while a sheet is protected"),
1027 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1028 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
,
1029 g_param_spec_boolean ("protected-allow-select-locked-cells",
1030 P_("Protected allow select locked cells"),
1031 P_("Allow the user to select locked cells while a sheet is protected"),
1032 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1033 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SORT_RANGES
,
1034 g_param_spec_boolean ("protected-allow-sort-ranges",
1035 P_("Protected allow sort ranges"),
1036 P_("Allow ranges to be sorted while a sheet is protected"),
1037 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1038 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
,
1039 g_param_spec_boolean ("protected-allow-edit-auto-filters",
1040 P_("Protected allow edit auto filters"),
1041 P_("Allow auto filters to be edited while a sheet is protected"),
1042 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1043 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
,
1044 g_param_spec_boolean ("protected-allow-edit-pivottable",
1045 P_("Protected allow edit pivottable"),
1046 P_("Allow pivottable to be edited while a sheet is protected"),
1047 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1048 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
,
1049 g_param_spec_boolean ("protected-allow-select-unlocked-cells",
1050 P_("Protected allow select unlocked cells"),
1051 P_("Allow the user to select unlocked cells while a sheet is protected"),
1052 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1054 g_object_class_install_property
1055 (gobject_class
, PROP_CONVENTIONS
,
1056 g_param_spec_boxed ("conventions",
1057 P_("Display convention for expressions (default Gnumeric A1)"),
1058 P_("How to format displayed expressions, (A1 vs R1C1, function names, ...)"),
1059 gnm_conventions_get_type (),
1061 G_PARAM_READWRITE
));
1062 g_object_class_install_property (gobject_class
, PROP_USE_R1C1
, /* convenience wrapper to CONVENTIONS */
1063 g_param_spec_boolean ("use-r1c1",
1064 P_("Display convention for expressions as XLS_R1C1 vs default"),
1065 P_("How to format displayed expressions, (a convenience api)"),
1068 G_PARAM_READWRITE
));
1070 g_object_class_install_property (gobject_class
, PROP_TAB_FOREGROUND
,
1071 g_param_spec_boxed ("tab-foreground",
1072 P_("Tab Foreground"),
1073 P_("The foreground color of the tab."),
1076 G_PARAM_READWRITE
));
1077 g_object_class_install_property (gobject_class
, PROP_TAB_BACKGROUND
,
1078 g_param_spec_boxed ("tab-background",
1079 P_("Tab Background"),
1080 P_("The background color of the tab."),
1083 G_PARAM_READWRITE
));
1085 /* What is this doing in sheet? */
1086 g_object_class_install_property (gobject_class
, PROP_ZOOM_FACTOR
,
1087 g_param_spec_double ("zoom-factor",
1089 P_("The level of zoom used for this sheet."),
1094 G_PARAM_READWRITE
));
1096 g_object_class_install_property (gobject_class
, PROP_COLUMNS
,
1097 g_param_spec_int ("columns",
1099 P_("Columns number in the sheet"),
1100 0, GNM_MAX_COLS
, GNM_DEFAULT_COLS
,
1101 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1103 g_object_class_install_property (gobject_class
, PROP_ROWS
,
1104 g_param_spec_int ("rows",
1106 P_("Rows number in the sheet"),
1107 0, GNM_MAX_ROWS
, GNM_DEFAULT_ROWS
,
1108 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1110 signals
[DETACHED_FROM_WORKBOOK
] = g_signal_new
1111 ("detached_from_workbook",
1114 G_STRUCT_OFFSET (GnmSheetClass
, detached_from_workbook
),
1116 g_cclosure_marshal_VOID__OBJECT
,
1117 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
1121 GSF_CLASS (GnmSheet
, gnm_sheet
,
1122 gnm_sheet_class_init
, gnm_sheet_init
, G_TYPE_OBJECT
)
1124 /* ------------------------------------------------------------------------- */
1127 gnm_sheet_type_get_type (void)
1129 static GType etype
= 0;
1131 static const GEnumValue values
[] = {
1132 { GNM_SHEET_DATA
, "GNM_SHEET_DATA", "data" },
1133 { GNM_SHEET_OBJECT
, "GNM_SHEET_OBJECT", "object" },
1134 { GNM_SHEET_XLM
, "GNM_SHEET_XLM", "xlm" },
1137 etype
= g_enum_register_static ("GnmSheetType", values
);
1143 gnm_sheet_visibility_get_type (void)
1145 static GType etype
= 0;
1147 static GEnumValue
const values
[] = {
1148 { GNM_SHEET_VISIBILITY_VISIBLE
, "GNM_SHEET_VISIBILITY_VISIBLE", "visible" },
1149 { GNM_SHEET_VISIBILITY_HIDDEN
, "GNM_SHEET_VISIBILITY_HIDDEN", "hidden" },
1150 { GNM_SHEET_VISIBILITY_VERY_HIDDEN
, "GNM_SHEET_VISIBILITY_VERY_HIDDEN", "very-hidden" },
1153 etype
= g_enum_register_static ("GnmSheetVisibility", values
);
1158 /* ------------------------------------------------------------------------- */
1163 return i
> 0 && (i
& (i
- 1)) == 0;
1167 gnm_sheet_valid_size (int cols
, int rows
)
1169 return (cols
>= GNM_MIN_COLS
&&
1170 cols
<= GNM_MAX_COLS
&&
1172 rows
>= GNM_MIN_ROWS
&&
1173 rows
<= GNM_MAX_ROWS
&&
1176 && 0x80000000u
/ (unsigned)(cols
/ 2) >= (unsigned)rows
1182 * gnm_sheet_suggest_size:
1183 * @cols: (inout): number of columns
1184 * @rows: (inout): number of rows
1186 * This function produces a valid sheet valid that is reasonable for data
1187 * of @cols columns by @rows rows. If possible, this will be a size bigger
1188 * in both dimensions. However, that is not always possible and when it is
1189 * not, the suggested will be smaller in one or both directions.
1192 gnm_sheet_suggest_size (int *cols
, int *rows
)
1194 int c
= GNM_DEFAULT_COLS
;
1195 int r
= GNM_DEFAULT_ROWS
;
1197 while (c
< *cols
&& c
< GNM_MAX_COLS
)
1200 while (r
< *rows
&& r
< GNM_MAX_ROWS
)
1203 while (!gnm_sheet_valid_size (c
, r
)) {
1204 /* Darn! Too large. */
1205 if (*cols
>= GNM_MIN_COLS
&& c
> GNM_MIN_COLS
)
1207 else if (*rows
>= GNM_MIN_ROWS
&& r
> GNM_MIN_ROWS
)
1209 else if (c
> GNM_MIN_COLS
)
1219 static void gnm_sheet_resize_main (Sheet
*sheet
, int cols
, int rows
,
1220 GOCmdContext
*cc
, GOUndo
**pundo
);
1223 cb_sheet_resize (Sheet
*sheet
, const GnmSheetSize
*data
, GOCmdContext
*cc
)
1225 gnm_sheet_resize_main (sheet
, data
->max_cols
, data
->max_rows
,
1230 gnm_sheet_resize_main (Sheet
*sheet
, int cols
, int rows
,
1231 GOCmdContext
*cc
, GOUndo
**pundo
)
1233 int old_cols
, old_rows
;
1234 GnmStyle
**common_col_styles
= NULL
;
1235 GnmStyle
**common_row_styles
= NULL
;
1237 if (pundo
) *pundo
= NULL
;
1239 old_cols
= gnm_sheet_get_max_cols (sheet
);
1240 old_rows
= gnm_sheet_get_max_rows (sheet
);
1241 if (old_cols
== cols
&& old_rows
== rows
)
1244 /* ---------------------------------------- */
1245 /* Gather styles we want to copy into new areas. */
1247 if (cols
> old_cols
) {
1249 common_row_styles
= sheet_style_most_common (sheet
, FALSE
);
1250 for (r
= 0; r
< old_rows
; r
++)
1251 gnm_style_ref (common_row_styles
[r
]);
1253 if (rows
> old_rows
) {
1255 common_col_styles
= sheet_style_most_common (sheet
, TRUE
);
1256 for (c
= 0; c
< old_cols
; c
++)
1257 gnm_style_ref (common_col_styles
[c
]);
1260 /* ---------------------------------------- */
1261 /* Remove the columns and rows that will disappear. */
1263 if (cols
< old_cols
) {
1267 err
= sheet_delete_cols (sheet
, cols
, G_MAXINT
,
1268 pundo
? &u
: NULL
, cc
);
1270 *pundo
= go_undo_combine (*pundo
, u
);
1275 if (rows
< old_rows
) {
1279 err
= sheet_delete_rows (sheet
, rows
, G_MAXINT
,
1280 pundo
? &u
: NULL
, cc
);
1282 *pundo
= go_undo_combine (*pundo
, u
);
1287 /* ---------------------------------------- */
1288 /* Restrict selection. (Not undone.) */
1290 SHEET_FOREACH_VIEW (sheet
, sv
,
1294 GSList
*sel
= selection_get_ranges (sv
, TRUE
);
1295 gboolean any
= FALSE
;
1297 sv_selection_reset (sv
);
1298 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
1299 vis
= new_full
.start
;
1300 for (l
= sel
; l
; l
= l
->next
) {
1301 GnmRange
*r
= l
->data
;
1303 if (range_intersection (&newr
, r
, &new_full
)) {
1304 sv_selection_add_range (sv
, &newr
);
1312 sv_selection_add_pos (sv
, 0, 0,
1313 GNM_SELECTION_MODE_ADD
);
1314 gnm_sheet_view_make_cell_visible (sv
, vis
.col
, vis
.row
, FALSE
);
1317 /* ---------------------------------------- */
1318 /* Resize column and row containers. */
1320 col_row_collection_resize (&sheet
->cols
, cols
);
1321 col_row_collection_resize (&sheet
->rows
, rows
);
1323 /* ---------------------------------------- */
1324 /* Resize the dependency containers. */
1327 GSList
*l
, *linked
= NULL
;
1328 /* FIXME: what about dependents in other workbooks? */
1329 WORKBOOK_FOREACH_DEPENDENT
1330 (sheet
->workbook
, dep
,
1332 if (dependent_is_linked (dep
)) {
1333 dependent_unlink (dep
);
1334 linked
= g_slist_prepend (linked
, dep
);
1337 gnm_dep_container_resize (sheet
->deps
, rows
);
1339 for (l
= linked
; l
; l
= l
->next
) {
1340 GnmDependent
*dep
= l
->data
;
1341 dependent_link (dep
);
1344 g_slist_free (linked
);
1346 workbook_queue_all_recalc (sheet
->workbook
);
1349 /* ---------------------------------------- */
1350 /* Resize the styles. */
1352 sheet_style_resize (sheet
, cols
, rows
);
1354 /* ---------------------------------------- */
1355 /* Actually change the properties. */
1357 sheet
->size
.max_cols
= cols
;
1358 sheet
->cols
.max_used
= MIN (sheet
->cols
.max_used
, cols
- 1);
1359 sheet
->size
.max_rows
= rows
;
1360 sheet
->rows
.max_used
= MIN (sheet
->rows
.max_used
, rows
- 1);
1362 if (old_cols
!= cols
)
1363 g_object_notify (G_OBJECT (sheet
), "columns");
1364 if (old_rows
!= rows
)
1365 g_object_notify (G_OBJECT (sheet
), "rows");
1368 GnmSheetSize
*data
= g_new (GnmSheetSize
, 1);
1371 data
->max_cols
= old_cols
;
1372 data
->max_rows
= old_rows
;
1373 u
= go_undo_binary_new (sheet
, data
,
1374 (GOUndoBinaryFunc
)cb_sheet_resize
,
1376 *pundo
= go_undo_combine (*pundo
, u
);
1379 range_init_full_sheet (&sheet
->priv
->unhidden_region
, sheet
);
1381 /* ---------------------------------------- */
1382 /* Apply styles to new areas. */
1384 if (cols
> old_cols
) {
1386 while (r
< old_rows
) {
1388 GnmStyle
*mstyle
= common_row_styles
[r
];
1390 while (r2
+ 1 < old_rows
&&
1391 mstyle
== common_row_styles
[r2
+ 1])
1393 range_init (&rng
, old_cols
, r
, cols
- 1, r2
);
1394 gnm_style_ref (mstyle
);
1395 sheet_apply_style (sheet
, &rng
, mstyle
);
1399 for (r
= 0; r
< old_rows
; r
++)
1400 gnm_style_unref (common_row_styles
[r
]);
1402 g_free (common_row_styles
);
1405 if (rows
> old_rows
) {
1408 while (c
< old_cols
) {
1410 GnmStyle
*mstyle
= common_col_styles
[c
];
1412 while (c2
+ 1 < old_cols
&&
1413 mstyle
== common_col_styles
[c2
+ 1])
1415 range_init (&rng
, c
, old_rows
, c2
, rows
- 1);
1416 gnm_style_ref (mstyle
);
1417 sheet_apply_style (sheet
, &rng
, mstyle
);
1421 if (cols
> old_cols
) {
1423 * Expanded in both directions. One could argue about
1424 * what style to use down here, but we choose the
1425 * last column style.
1427 GnmStyle
*mstyle
= common_col_styles
[old_cols
- 1];
1432 cols
- 1, rows
- 1);
1433 gnm_style_ref (mstyle
);
1434 sheet_apply_style (sheet
, &rng
, mstyle
);
1437 for (c
= 0; c
< old_cols
; c
++)
1438 gnm_style_unref (common_col_styles
[c
]);
1439 g_free (common_col_styles
);
1442 /* ---------------------------------------- */
1444 sheet_redraw_all (sheet
, TRUE
);
1449 go_undo_undo_with_data (*pundo
, cc
);
1450 g_object_unref (*pundo
);
1458 * @cols: the new columns number.
1459 * @rows: the new rows number.
1460 * @cc: #GOCmdContext.
1461 * @perr: (out): will be %TRUE on error.
1463 * Returns: (transfer full): the newly allocated #GOUndo.
1466 gnm_sheet_resize (Sheet
*sheet
, int cols
, int rows
,
1467 GOCmdContext
*cc
, gboolean
*perr
)
1469 GOUndo
*undo
= NULL
;
1471 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1472 g_return_val_if_fail (gnm_sheet_valid_size (cols
, rows
), NULL
);
1474 if (cols
< sheet
->size
.max_cols
|| rows
< sheet
->size
.max_rows
) {
1475 GSList
*overlap
, *l
;
1476 gboolean bad
= FALSE
;
1479 r
.start
.col
= r
.start
.row
= 0;
1480 r
.end
.col
= MIN (cols
, sheet
->size
.max_cols
) - 1;
1481 r
.end
.row
= MIN (rows
, sheet
->size
.max_rows
) - 1;
1483 overlap
= gnm_sheet_merge_get_overlap (sheet
, &r
);
1484 for (l
= overlap
; l
&& !bad
; l
= l
->next
) {
1485 GnmRange
const *m
= l
->data
;
1486 if (!range_contained (m
, &r
)) {
1488 gnm_cmd_context_error_splits_merge (cc
, m
);
1491 g_slist_free (overlap
);
1498 gnm_sheet_resize_main (sheet
, cols
, rows
, cc
, &undo
);
1506 * sheet_new_with_type:
1508 * @name: An unquoted name
1509 * @type: @GnmSheetType
1510 * @columns: The number of columns for the sheet
1511 * @rows: The number of rows for the sheet
1513 * Create a new Sheet of type @type, and associate it with @wb.
1514 * The type cannot be changed later.
1516 * Returns: (transfer full): the newly allocated sheet.
1519 sheet_new_with_type (Workbook
*wb
, char const *name
, GnmSheetType type
,
1520 int columns
, int rows
)
1524 g_return_val_if_fail (wb
!= NULL
, NULL
);
1525 g_return_val_if_fail (name
!= NULL
, NULL
);
1526 g_return_val_if_fail (gnm_sheet_valid_size (columns
, rows
), NULL
);
1528 sheet
= g_object_new (GNM_SHEET_TYPE
,
1534 "zoom-factor", gnm_conf_get_core_gui_window_zoom (),
1537 if (type
== GNM_SHEET_OBJECT
)
1538 print_info_set_paper_orientation (sheet
->print_info
, GTK_PAGE_ORIENTATION_LANDSCAPE
);
1546 * @name: The name for the sheet (unquoted).
1547 * @columns: The requested columns number.
1548 * @rows: The requested rows number.
1550 * Create a new Sheet of type SHEET_DATA, and associate it with @wb.
1551 * The type can not be changed later
1553 * Returns: (transfer full): the newly allocated sheet.
1556 sheet_new (Workbook
*wb
, char const *name
, int columns
, int rows
)
1558 return sheet_new_with_type (wb
, name
, GNM_SHEET_DATA
, columns
, rows
);
1561 /****************************************************************************/
1564 sheet_redraw_all (Sheet
const *sheet
, gboolean headers
)
1566 /* We potentially do a lot of recalcs as part of this, so make sure
1567 stuff that caches sub-computations see the whole thing instead
1568 of clearing between cells. */
1569 gnm_app_recalc_start ();
1570 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1571 sc_redraw_all (control
, headers
););
1572 gnm_app_recalc_finish ();
1576 cb_clear_rendered_values (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
1578 gnm_cell_unrender (iter
->cell
);
1583 * sheet_range_calc_spans:
1584 * @sheet: The sheet,
1585 * @r: the region to update.
1588 * This is used to re-calculate cell dimensions and re-render
1589 * a cell's text. eg. if a format has changed we need to re-render
1590 * the cached version of the rendered text in the cell.
1593 sheet_range_calc_spans (Sheet
*sheet
, GnmRange
const *r
, GnmSpanCalcFlags flags
)
1595 if (flags
& GNM_SPANCALC_RE_RENDER
)
1596 sheet_foreach_cell_in_range
1597 (sheet
, CELL_ITER_IGNORE_NONEXISTENT
, r
,
1598 cb_clear_rendered_values
, NULL
);
1599 sheet_queue_respan (sheet
, r
->start
.row
, r
->end
.row
);
1601 /* Redraw the new region in case the span changes */
1602 sheet_redraw_range (sheet
, r
);
1606 sheet_redraw_partial_row (Sheet
const *sheet
, int const row
,
1607 int const start_col
, int const end_col
)
1610 range_init (&r
, start_col
, row
, end_col
, row
);
1611 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1612 sc_redraw_range (control
, &r
););
1616 sheet_redraw_cell (GnmCell
const *cell
)
1618 CellSpanInfo
const * span
;
1619 int start_col
, end_col
, row
;
1620 GnmRange
const *merged
;
1624 g_return_if_fail (cell
!= NULL
);
1626 sheet
= cell
->base
.sheet
;
1627 merged
= gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
1628 if (merged
!= NULL
) {
1629 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1630 sc_redraw_range (control
, merged
););
1634 row
= cell
->pos
.row
;
1635 start_col
= end_col
= cell
->pos
.col
;
1636 ri
= sheet_row_get (sheet
, row
);
1637 span
= row_span_get (ri
, start_col
);
1640 start_col
= span
->left
;
1641 end_col
= span
->right
;
1644 sheet_redraw_partial_row (sheet
, row
, start_col
, end_col
);
1648 sheet_cell_calc_span (GnmCell
*cell
, GnmSpanCalcFlags flags
)
1650 CellSpanInfo
const * span
;
1652 int min_col
, max_col
, row
;
1653 gboolean render
= (flags
& GNM_SPANCALC_RE_RENDER
) != 0;
1654 gboolean
const resize
= (flags
& GNM_SPANCALC_RESIZE
) != 0;
1655 gboolean existing
= FALSE
;
1656 GnmRange
const *merged
;
1660 g_return_if_fail (cell
!= NULL
);
1662 sheet
= cell
->base
.sheet
;
1663 row
= cell
->pos
.row
;
1665 /* Render and Size any unrendered cells */
1666 if ((flags
& GNM_SPANCALC_RENDER
) && gnm_cell_get_rendered_value (cell
) == NULL
)
1670 if (!gnm_cell_has_expr (cell
))
1671 gnm_cell_render_value ((GnmCell
*)cell
, TRUE
);
1673 gnm_cell_unrender (cell
);
1674 } else if (resize
) {
1675 /* FIXME: what was wanted here? */
1676 /* rendered_value_calc_size (cell); */
1679 /* Is there an existing span ? clear it BEFORE calculating new one */
1680 ri
= sheet_row_get (sheet
, row
);
1681 span
= row_span_get (ri
, cell
->pos
.col
);
1683 GnmCell
const * const other
= span
->cell
;
1685 min_col
= span
->left
;
1686 max_col
= span
->right
;
1688 /* A different cell used to span into this cell, respan that */
1689 if (cell
!= other
) {
1690 int other_left
, other_right
;
1692 cell_unregister_span (other
);
1693 cell_calc_span (other
, &other_left
, &other_right
);
1694 if (min_col
> other_left
)
1695 min_col
= other_left
;
1696 if (max_col
< other_right
)
1697 max_col
= other_right
;
1699 if (other_left
!= other_right
)
1700 cell_register_span (other
, other_left
, other_right
);
1704 min_col
= max_col
= cell
->pos
.col
;
1706 merged
= gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
1707 if (NULL
!= merged
) {
1709 if (min_col
> merged
->start
.col
)
1710 min_col
= merged
->start
.col
;
1711 if (max_col
< merged
->end
.col
)
1712 max_col
= merged
->end
.col
;
1714 sheet_redraw_cell (cell
);
1718 /* Calculate the span of the cell */
1719 cell_calc_span (cell
, &left
, &right
);
1722 if (max_col
< right
)
1725 /* This cell already had an existing span */
1727 /* If it changed, remove the old one */
1728 if (left
!= span
->left
|| right
!= span
->right
)
1729 cell_unregister_span (cell
);
1731 /* unchanged, short curcuit adding the span again */
1736 cell_register_span (cell
, left
, right
);
1739 sheet_redraw_partial_row (sheet
, row
, min_col
, max_col
);
1743 * sheet_apply_style: (skip)
1744 * @sheet: the sheet in which can be found
1745 * @range: the range to which should be applied
1746 * @style: (transfer full): A #GnmStyle partial style
1748 * A mid level routine that applies the supplied partial style @style to the
1749 * target @range and performs the necessary respanning and redrawing.
1752 sheet_apply_style (Sheet
*sheet
,
1753 GnmRange
const *range
,
1756 GnmSpanCalcFlags spanflags
= gnm_style_required_spanflags (style
);
1757 sheet_style_apply_range (sheet
, range
, style
);
1758 /* This also redraws the range: */
1759 sheet_range_calc_spans (sheet
, range
, spanflags
);
1763 * sheet_apply_style_gi: (rename-to sheet_apply_style)
1764 * @sheet: the sheet in which can be found
1765 * @range: the range to which should be applied
1766 * @style: A #GnmStyle partial style
1768 * A mid level routine that applies the supplied partial style @style to the
1769 * target @range and performs the necessary respanning and redrawing.
1772 sheet_apply_style_gi (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*style
)
1774 GnmSpanCalcFlags spanflags
= gnm_style_required_spanflags (style
);
1775 gnm_style_ref (style
);
1776 sheet_style_apply_range (sheet
, range
, style
);
1777 /* This also redraws the range: */
1778 sheet_range_calc_spans (sheet
, range
, spanflags
);
1782 sheet_apply_style_cb (GnmSheetRange
*sr
,
1785 gnm_style_ref (style
);
1786 sheet_apply_style (sr
->sheet
, &sr
->range
, style
);
1787 sheet_flag_style_update_range (sr
->sheet
, &sr
->range
);
1791 * sheet_apply_style_undo:
1792 * @sr: (transfer full): #GnmSheetRange
1793 * @style: (transfer none): #GnmStyle
1795 * Returns: (transfer full): the new #GOUndo.
1798 sheet_apply_style_undo (GnmSheetRange
*sr
,
1801 gnm_style_ref (style
);
1802 return go_undo_binary_new
1803 (sr
, (gpointer
)style
,
1804 (GOUndoBinaryFunc
) sheet_apply_style_cb
,
1805 (GFreeFunc
) gnm_sheet_range_free
,
1806 (GFreeFunc
) gnm_style_unref
);
1812 sheet_apply_border (Sheet
*sheet
,
1813 GnmRange
const *range
,
1814 GnmBorder
**borders
)
1816 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_RE_RENDER
| GNM_SPANCALC_RESIZE
;
1817 sheet_style_apply_border (sheet
, range
, borders
);
1818 /* This also redraws the range: */
1819 sheet_range_calc_spans (sheet
, range
, spanflags
);
1822 /****************************************************************************/
1825 sheet_row_new (Sheet
*sheet
)
1829 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1831 ri
= col_row_info_new ();
1832 *ri
= sheet
->rows
.default_style
;
1833 ri
->is_default
= FALSE
;
1834 ri
->needs_respan
= TRUE
;
1840 sheet_col_new (Sheet
*sheet
)
1844 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1846 ci
= col_row_info_new ();
1847 *ci
= sheet
->cols
.default_style
;
1848 ci
->is_default
= FALSE
;
1854 sheet_colrow_add (Sheet
*sheet
, ColRowInfo
*cp
, gboolean is_cols
, int n
)
1856 ColRowCollection
*info
= is_cols
? &sheet
->cols
: &sheet
->rows
;
1857 ColRowSegment
**psegment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (info
, n
);
1859 g_return_if_fail (n
>= 0);
1860 g_return_if_fail (n
< colrow_max (is_cols
, sheet
));
1862 if (*psegment
== NULL
)
1863 *psegment
= g_new0 (ColRowSegment
, 1);
1864 colrow_free ((*psegment
)->info
[COLROW_SUB_INDEX (n
)]);
1865 (*psegment
)->info
[COLROW_SUB_INDEX (n
)] = cp
;
1867 if (cp
->outline_level
> info
->max_outline_level
)
1868 info
->max_outline_level
= cp
->outline_level
;
1869 if (n
> info
->max_used
) {
1871 sheet
->priv
->resize_scrollbar
= TRUE
;
1876 sheet_reposition_objects (Sheet
const *sheet
, GnmCellPos
const *pos
)
1879 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1880 sheet_object_update_bounds (GNM_SO (ptr
->data
), pos
);
1884 * sheet_flag_status_update_cell:
1885 * @cell: The cell that has changed.
1887 * flag the sheet as requiring an update to the status display
1888 * if the supplied cell location is the edit cursor, or part of the
1891 * Will cause the format toolbar, the edit area, and the auto expressions to be
1892 * updated if appropriate.
1895 sheet_flag_status_update_cell (GnmCell
const *cell
)
1897 SHEET_FOREACH_VIEW (cell
->base
.sheet
, sv
,
1898 gnm_sheet_view_flag_status_update_pos (sv
, &cell
->pos
););
1902 * sheet_flag_status_update_range:
1904 * @range: (nullable): GnmRange, or %NULL for full sheet
1906 * flag the sheet as requiring an update to the status display
1907 * if the supplied cell location contains the edit cursor, or intersects of
1908 * the selected region.
1910 * Will cause the format toolbar, the edit area, and the auto expressions to be
1911 * updated if appropriate.
1914 sheet_flag_status_update_range (Sheet
const *sheet
, GnmRange
const *range
)
1916 SHEET_FOREACH_VIEW (sheet
, sv
,
1917 gnm_sheet_view_flag_status_update_range (sv
, range
););
1921 * sheet_flag_style_update_range:
1922 * @sheet: The sheet being changed
1923 * @range: the range that is changing.
1925 * Flag format changes that will require updating the format indicators.
1928 sheet_flag_style_update_range (Sheet
const *sheet
, GnmRange
const *range
)
1930 SHEET_FOREACH_VIEW (sheet
, sv
,
1931 gnm_sheet_view_flag_style_update_range (sv
, range
););
1935 * sheet_flag_recompute_spans:
1938 * Flag the sheet as requiring a full span recomputation the next time
1939 * sheet_update is called.
1942 sheet_flag_recompute_spans (Sheet
const *sheet
)
1944 sheet
->priv
->recompute_spans
= TRUE
;
1948 cb_outline_level (GnmColRowIter
const *iter
, gpointer data
)
1950 int *outline_level
= data
;
1951 if (*outline_level
< iter
->cri
->outline_level
)
1952 *outline_level
= iter
->cri
->outline_level
;
1957 * sheet_colrow_fit_gutter:
1958 * @sheet: Sheet to change.
1959 * @is_cols: %TRUE for columns, %FALSE for rows.
1961 * Find the current max outline level.
1964 sheet_colrow_fit_gutter (Sheet
const *sheet
, gboolean is_cols
)
1966 int outline_level
= 0;
1967 sheet_colrow_foreach (sheet
, is_cols
, 0, -1,
1968 cb_outline_level
, &outline_level
);
1969 return outline_level
;
1973 * sheet_update_only_grid:
1976 * Should be called after a logical command has finished processing
1977 * to request redraws for any pending events
1980 sheet_update_only_grid (Sheet
const *sheet
)
1984 g_return_if_fail (IS_SHEET (sheet
));
1988 /* be careful these can toggle flags */
1989 if (p
->recompute_max_col_group
) {
1990 sheet_colrow_gutter ((Sheet
*)sheet
, TRUE
,
1991 sheet_colrow_fit_gutter (sheet
, TRUE
));
1992 sheet
->priv
->recompute_max_col_group
= FALSE
;
1994 if (p
->recompute_max_row_group
) {
1995 sheet_colrow_gutter ((Sheet
*)sheet
, FALSE
,
1996 sheet_colrow_fit_gutter (sheet
, FALSE
));
1997 sheet
->priv
->recompute_max_row_group
= FALSE
;
2000 SHEET_FOREACH_VIEW (sheet
, sv
, {
2001 if (sv
->reposition_selection
) {
2002 sv
->reposition_selection
= FALSE
;
2004 /* when moving we cleared the selection before
2007 if (sv
->selections
!= NULL
)
2008 sv_selection_set (sv
, &sv
->edit_pos_real
,
2009 sv
->cursor
.base_corner
.col
,
2010 sv
->cursor
.base_corner
.row
,
2011 sv
->cursor
.move_corner
.col
,
2012 sv
->cursor
.move_corner
.row
);
2016 if (p
->recompute_spans
) {
2017 p
->recompute_spans
= FALSE
;
2018 /* FIXME : I would prefer to use GNM_SPANCALC_RENDER rather than
2019 * RE_RENDER. It only renders those cells which are not
2020 * rendered. The trouble is that when a col changes size we
2021 * need to rerender, but currently nothing marks that.
2023 * hmm, that suggests an approach. maybe I can install a per
2024 * col flag. Then add a flag clearing loop after the
2028 sheet_calc_spans (sheet
, GNM_SPANCALC_RESIZE
|GNM_SPANCALC_RE_RENDER
|
2029 (p
->recompute_visibility
?
2030 SPANCALC_NO_DRAW
: GNM_SPANCALC_SIMPLE
));
2032 sheet_queue_respan (sheet
, 0, gnm_sheet_get_last_row (sheet
));
2035 if (p
->reposition_objects
.row
< gnm_sheet_get_max_rows (sheet
) ||
2036 p
->reposition_objects
.col
< gnm_sheet_get_max_cols (sheet
)) {
2037 SHEET_FOREACH_VIEW (sheet
, sv
, {
2038 if (!p
->resize
&& gnm_sheet_view_is_frozen (sv
)) {
2039 if (p
->reposition_objects
.col
< sv
->unfrozen_top_left
.col
||
2040 p
->reposition_objects
.row
< sv
->unfrozen_top_left
.row
) {
2041 gnm_sheet_view_resize (sv
, FALSE
);
2045 sheet_reposition_objects (sheet
, &p
->reposition_objects
);
2046 p
->reposition_objects
.row
= gnm_sheet_get_max_rows (sheet
);
2047 p
->reposition_objects
.col
= gnm_sheet_get_max_cols (sheet
);
2052 SHEET_FOREACH_VIEW (sheet
, sv
, { gnm_sheet_view_resize (sv
, FALSE
); });
2055 if (p
->recompute_visibility
) {
2056 /* TODO : There is room for some opimization
2057 * We only need to force complete visibility recalculation
2058 * (which we do in sheet_compute_visible_region)
2059 * if a row or col before the start of the visible region.
2060 * If we are REALLY smart we could even accumulate the size differential
2063 p
->recompute_visibility
= FALSE
;
2064 p
->resize_scrollbar
= FALSE
; /* compute_visible_region does this */
2065 SHEET_FOREACH_CONTROL(sheet
, view
, control
,
2066 sc_recompute_visible_region (control
, TRUE
););
2067 sheet_redraw_all (sheet
, TRUE
);
2070 if (p
->resize_scrollbar
) {
2071 sheet_scrollbar_config (sheet
);
2072 p
->resize_scrollbar
= FALSE
;
2074 if (p
->filters_changed
) {
2075 p
->filters_changed
= FALSE
;
2076 SHEET_FOREACH_CONTROL (sheet
, sv
, sc
,
2077 wb_control_menu_state_update (sc_wbc (sc
), MS_ADD_VS_REMOVE_FILTER
););
2085 * Should be called after a logical command has finished processing to request
2086 * redraws for any pending events, and to update the various status regions
2089 sheet_update (Sheet
const *sheet
)
2091 g_return_if_fail (IS_SHEET (sheet
));
2093 sheet_update_only_grid (sheet
);
2095 SHEET_FOREACH_VIEW (sheet
, sv
, gnm_sheet_view_update (sv
););
2100 * @sheet: The sheet where we want to locate the cell
2101 * @col: the cell column
2102 * @row: the cell row
2104 * Return value: (nullable): a #GnmCell, or %NULL if the cell does not exist
2107 sheet_cell_get (Sheet
const *sheet
, int col
, int row
)
2112 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2116 cell
= g_hash_table_lookup (sheet
->cell_hash
, &key
);
2123 * @sheet: The sheet where we want to locate the cell
2124 * @col: the cell column
2125 * @row: the cell row
2127 * Return value: a #GnmCell containing at (@col,@row).
2128 * If no cell existed at that location before, it is created.
2131 sheet_cell_fetch (Sheet
*sheet
, int col
, int row
)
2135 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2137 cell
= sheet_cell_get (sheet
, col
, row
);
2139 cell
= sheet_cell_create (sheet
, col
, row
);
2145 * sheet_colrow_can_group:
2148 * @is_cols: %TRUE for columns, %FALSE for rows.
2150 * Returns: %TRUE if the cols/rows in @r.start -> @r.end can be grouped,
2151 * %FALSE otherwise. You can invert the result if you need to find out if a
2152 * group can be ungrouped.
2155 sheet_colrow_can_group (Sheet
*sheet
, GnmRange
const *r
, gboolean is_cols
)
2157 ColRowInfo
const *start_cri
, *end_cri
;
2160 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
2163 start
= r
->start
.col
;
2166 start
= r
->start
.row
;
2169 start_cri
= sheet_colrow_fetch (sheet
, start
, is_cols
);
2170 end_cri
= sheet_colrow_fetch (sheet
, end
, is_cols
);
2172 /* Groups on outline level 0 (no outline) may always be formed */
2173 if (start_cri
->outline_level
== 0 || end_cri
->outline_level
== 0)
2176 /* We just won't group a group that already exists (or doesn't), it's useless */
2177 return (colrow_find_outline_bound (sheet
, is_cols
, start
, start_cri
->outline_level
, FALSE
) != start
||
2178 colrow_find_outline_bound (sheet
, is_cols
, end
, end_cri
->outline_level
, TRUE
) != end
);
2182 sheet_colrow_group_ungroup (Sheet
*sheet
, GnmRange
const *r
,
2183 gboolean is_cols
, gboolean group
)
2185 int i
, new_max
, start
, end
;
2186 int const step
= group
? 1 : -1;
2188 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
2190 /* Can we group/ungroup ? */
2191 if (group
!= sheet_colrow_can_group (sheet
, r
, is_cols
))
2195 start
= r
->start
.col
;
2198 start
= r
->start
.row
;
2202 /* Set new outline for each col/row and find highest outline level */
2203 new_max
= (is_cols
? &sheet
->cols
: &sheet
->rows
)->max_outline_level
;
2204 for (i
= start
; i
<= end
; i
++) {
2205 ColRowInfo
*cri
= sheet_colrow_fetch (sheet
, i
, is_cols
);
2206 int const new_level
= cri
->outline_level
+ step
;
2208 if (new_level
>= 0) {
2209 col_row_info_set_outline (cri
, new_level
, FALSE
);
2210 if (new_max
< new_level
)
2211 new_max
= new_level
;
2216 new_max
= sheet_colrow_fit_gutter (sheet
, is_cols
);
2218 sheet_colrow_gutter (sheet
, is_cols
, new_max
);
2219 SHEET_FOREACH_VIEW (sheet
, sv
,
2220 gnm_sheet_view_redraw_headers (sv
, is_cols
, !is_cols
, NULL
););
2226 * sheet_colrow_gutter:
2228 * @is_cols: %TRUE for columns, %FALSE for rows.
2231 * Set the maximum outline levels for cols or rows.
2234 sheet_colrow_gutter (Sheet
*sheet
, gboolean is_cols
, int max_outline
)
2236 ColRowCollection
*infos
;
2238 g_return_if_fail (IS_SHEET (sheet
));
2240 infos
= is_cols
? &(sheet
->cols
) : &(sheet
->rows
);
2241 if (infos
->max_outline_level
!= max_outline
) {
2242 sheet
->priv
->resize
= TRUE
;
2243 infos
->max_outline_level
= max_outline
;
2247 struct sheet_extent_data
{
2249 gboolean spans_and_merges_extend
;
2250 gboolean ignore_empties
;
2251 gboolean include_hidden
;
2255 cb_sheet_get_extent (G_GNUC_UNUSED gpointer ignored
, gpointer value
, gpointer data
)
2257 GnmCell
const *cell
= (GnmCell
const *) value
;
2258 struct sheet_extent_data
*res
= data
;
2259 Sheet
*sheet
= cell
->base
.sheet
;
2260 ColRowInfo
*ri
= NULL
;
2262 if (res
->ignore_empties
&& gnm_cell_is_empty (cell
))
2264 if (!res
->include_hidden
) {
2265 ri
= sheet_col_get (sheet
, cell
->pos
.col
);
2268 ri
= sheet_row_get (sheet
, cell
->pos
.row
);
2273 /* Remember the first cell is the min and max */
2274 if (res
->range
.start
.col
> cell
->pos
.col
)
2275 res
->range
.start
.col
= cell
->pos
.col
;
2276 if (res
->range
.end
.col
< cell
->pos
.col
)
2277 res
->range
.end
.col
= cell
->pos
.col
;
2278 if (res
->range
.start
.row
> cell
->pos
.row
)
2279 res
->range
.start
.row
= cell
->pos
.row
;
2280 if (res
->range
.end
.row
< cell
->pos
.row
)
2281 res
->range
.end
.row
= cell
->pos
.row
;
2283 if (!res
->spans_and_merges_extend
)
2286 /* Cannot span AND merge */
2287 if (gnm_cell_is_merged (cell
)) {
2288 GnmRange
const *merged
=
2289 gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
2290 res
->range
= range_union (&res
->range
, merged
);
2292 CellSpanInfo
const *span
;
2294 ri
= sheet_row_get (sheet
, cell
->pos
.row
);
2295 if (ri
->needs_respan
)
2296 row_calc_spans (ri
, cell
->pos
.row
, sheet
);
2297 span
= row_span_get (ri
, cell
->pos
.col
);
2299 if (res
->range
.start
.col
> span
->left
)
2300 res
->range
.start
.col
= span
->left
;
2301 if (res
->range
.end
.col
< span
->right
)
2302 res
->range
.end
.col
= span
->right
;
2310 * @spans_and_merges_extend: optionally extend region for spans and merges.
2311 * @include_hidden: whether to include the content of hidden cells.
2313 * calculates the area occupied by cell data.
2315 * NOTE: When spans_and_merges_extend is %TRUE, this function will calculate
2316 * all spans. That might be expensive.
2318 * NOTE: This refers to *visible* contents. Cells with empty values, including
2319 * formulas with such values, are *ignored.
2321 * Return value: the range.
2324 sheet_get_extent (Sheet
const *sheet
, gboolean spans_and_merges_extend
, gboolean include_hidden
)
2326 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2327 struct sheet_extent_data closure
;
2330 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2332 closure
.range
.start
.col
= gnm_sheet_get_last_col (sheet
) + 1;
2333 closure
.range
.start
.row
= gnm_sheet_get_last_row (sheet
) + 1;
2334 closure
.range
.end
.col
= 0;
2335 closure
.range
.end
.row
= 0;
2336 closure
.spans_and_merges_extend
= spans_and_merges_extend
;
2337 closure
.include_hidden
= include_hidden
;
2338 closure
.ignore_empties
= TRUE
;
2340 sheet_cell_foreach (sheet
, &cb_sheet_get_extent
, &closure
);
2342 for (ptr
= sheet
->sheet_objects
; ptr
; ptr
= ptr
->next
) {
2343 SheetObject
*so
= GNM_SO (ptr
->data
);
2345 closure
.range
.start
.col
= MIN (so
->anchor
.cell_bound
.start
.col
,
2346 closure
.range
.start
.col
);
2347 closure
.range
.start
.row
= MIN (so
->anchor
.cell_bound
.start
.row
,
2348 closure
.range
.start
.row
);
2349 closure
.range
.end
.col
= MAX (so
->anchor
.cell_bound
.end
.col
,
2350 closure
.range
.end
.col
);
2351 closure
.range
.end
.row
= MAX (so
->anchor
.cell_bound
.end
.row
,
2352 closure
.range
.end
.row
);
2355 if (closure
.range
.start
.col
> gnm_sheet_get_last_col (sheet
))
2356 closure
.range
.start
.col
= 0;
2357 if (closure
.range
.start
.row
> gnm_sheet_get_last_row (sheet
))
2358 closure
.range
.start
.row
= 0;
2359 if (closure
.range
.end
.col
< 0)
2360 closure
.range
.end
.col
= 0;
2361 if (closure
.range
.end
.row
< 0)
2362 closure
.range
.end
.row
= 0;
2364 return closure
.range
;
2368 * sheet_get_cells_extent:
2371 * Calculates the area occupied by cells, including empty cells.
2373 * Return value: the range.
2376 sheet_get_cells_extent (Sheet
const *sheet
)
2378 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2379 struct sheet_extent_data closure
;
2381 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2383 closure
.range
.start
.col
= gnm_sheet_get_last_col (sheet
);
2384 closure
.range
.start
.row
= gnm_sheet_get_last_row (sheet
);
2385 closure
.range
.end
.col
= 0;
2386 closure
.range
.end
.row
= 0;
2387 closure
.spans_and_merges_extend
= FALSE
;
2388 closure
.include_hidden
= TRUE
;
2389 closure
.ignore_empties
= FALSE
;
2391 sheet_cell_foreach (sheet
, &cb_sheet_get_extent
, &closure
);
2393 return closure
.range
;
2398 sheet_get_nominal_printarea (Sheet
const *sheet
)
2400 GnmNamedExpr
*nexpr
;
2404 GnmRangeRef
const *r_ref
;
2408 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2410 parse_pos_init_sheet (&pos
, sheet
);
2411 nexpr
= expr_name_lookup (&pos
, "Print_Area");
2415 val
= gnm_expr_top_get_range (nexpr
->texpr
);
2416 r_ref
= val
? value_get_rangeref (val
) : NULL
;
2417 if (r_ref
== NULL
) {
2418 value_release (val
);
2422 r
= g_new0 (GnmRange
, 1);
2423 range_init_rangeref (r
, r_ref
);
2424 value_release (val
);
2426 if (r
->end
.col
>= (max_cols
= gnm_sheet_get_max_cols (sheet
)))
2427 r
->end
.col
= max_cols
- 1;
2428 if (r
->end
.row
>= (max_rows
= gnm_sheet_get_max_rows (sheet
)))
2429 r
->end
.row
= max_rows
- 1;
2430 if (r
->start
.col
< 0)
2432 if (r
->start
.row
< 0)
2439 sheet_get_printarea (Sheet
const *sheet
,
2440 gboolean include_styles
,
2441 gboolean ignore_printarea
)
2443 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2444 GnmRange print_area
;
2446 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2448 if (!ignore_printarea
) {
2449 GnmRange
*r
= sheet_get_nominal_printarea (sheet
);
2457 print_area
= sheet_get_extent (sheet
, TRUE
, FALSE
);
2459 sheet_style_get_extent (sheet
, &print_area
);
2466 gboolean ignore_strings
;
2469 /* find the maximum width in a range. */
2471 cb_max_cell_width (GnmCellIter
const *iter
, struct cb_fit
*data
)
2474 GnmCell
*cell
= iter
->cell
;
2475 GnmRenderedValue
*rv
;
2477 if (gnm_cell_is_merged (cell
))
2481 * Special handling for manual recalc. We need to eval newly
2482 * entered expressions. gnm_cell_render_value will do that for us,
2483 * but we want to short-circuit some strings early.
2485 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
)
2486 gnm_cell_eval (cell
);
2488 if (data
->ignore_strings
&& VALUE_IS_STRING (cell
->value
))
2491 /* Variable width cell must be re-rendered */
2492 rv
= gnm_cell_get_rendered_value (cell
);
2493 if (rv
== NULL
|| rv
->variable_width
)
2494 gnm_cell_render_value (cell
, FALSE
);
2496 /* Make sure things are as-if drawn. */
2497 cell_finish_layout (cell
, NULL
, iter
->ci
->size_pixels
, TRUE
);
2499 width
= gnm_cell_rendered_width (cell
) + gnm_cell_rendered_offset (cell
);
2500 if (width
> data
->max
)
2507 * sheet_col_size_fit_pixels:
2509 * @col: the column that we want to query
2510 * @srow: starting row.
2511 * @erow: ending row.
2512 * @ignore_strings: skip cells containing string values.
2514 * This routine computes the ideal size for the column to make the contents all
2515 * cells in the column visible.
2517 * Returns: Maximum size in pixels INCLUDING margins and grid lines
2518 * or 0 if there are no cells.
2521 sheet_col_size_fit_pixels (Sheet
*sheet
, int col
, int srow
, int erow
,
2522 gboolean ignore_strings
)
2525 ColRowInfo
*ci
= sheet_col_get (sheet
, col
);
2530 data
.ignore_strings
= ignore_strings
;
2531 sheet_foreach_cell_in_region (sheet
,
2532 CELL_ITER_IGNORE_NONEXISTENT
|
2533 CELL_ITER_IGNORE_HIDDEN
|
2534 CELL_ITER_IGNORE_FILTERED
,
2535 col
, srow
, col
, erow
,
2536 (CellIterFunc
)&cb_max_cell_width
, &data
);
2538 /* Reset to the default width if the column was empty */
2542 /* GnmCell width does not include margins or far grid line*/
2543 return data
.max
+ GNM_COL_MARGIN
+ GNM_COL_MARGIN
+ 1;
2546 /* find the maximum height in a range. */
2548 cb_max_cell_height (GnmCellIter
const *iter
, struct cb_fit
*data
)
2551 GnmCell
*cell
= iter
->cell
;
2553 if (gnm_cell_is_merged (cell
))
2557 * Special handling for manual recalc. We need to eval newly
2558 * entered expressions. gnm_cell_render_value will do that for us,
2559 * but we want to short-circuit some strings early.
2561 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
)
2562 gnm_cell_eval (cell
);
2564 if (data
->ignore_strings
&& VALUE_IS_STRING (cell
->value
))
2567 if (!VALUE_IS_STRING (cell
->value
)) {
2569 * Mildly cheating to avoid performance problems, See bug
2570 * 359392. This assumes that non-strings do not wrap and
2571 * that they are all the same height, more or less.
2573 Sheet
const *sheet
= cell
->base
.sheet
;
2574 height
= gnm_style_get_pango_height (gnm_cell_get_style (cell
),
2575 sheet
->rendered_values
->context
,
2576 sheet
->last_zoom_factor_used
);
2578 (void)gnm_cell_fetch_rendered_value (cell
, TRUE
);
2580 /* Make sure things are as-if drawn. Inhibit #####s. */
2581 cell_finish_layout (cell
, NULL
, iter
->ci
->size_pixels
, FALSE
);
2583 height
= gnm_cell_rendered_height (cell
);
2586 if (height
> data
->max
)
2593 * sheet_row_size_fit_pixels:
2595 * @row: the row that we want to query
2596 * @scol: starting column.
2597 * @ecol: ending column.
2598 * @ignore_strings: skip cells containing string values.
2600 * This routine computes the ideal size for the row to make all data fit
2603 * Returns: Maximum size in pixels INCLUDING margins and grid lines
2604 * or 0 if there are no cells.
2607 sheet_row_size_fit_pixels (Sheet
*sheet
, int row
, int scol
, int ecol
,
2608 gboolean ignore_strings
)
2611 ColRowInfo
const *ri
= sheet_row_get (sheet
, row
);
2616 data
.ignore_strings
= ignore_strings
;
2617 sheet_foreach_cell_in_region (sheet
,
2618 CELL_ITER_IGNORE_NONEXISTENT
|
2619 CELL_ITER_IGNORE_HIDDEN
|
2620 CELL_ITER_IGNORE_FILTERED
,
2623 (CellIterFunc
)&cb_max_cell_height
, &data
);
2625 /* Reset to the default width if the column was empty */
2629 /* GnmCell height does not include margins or bottom grid line */
2630 return data
.max
+ GNM_ROW_MARGIN
+ GNM_ROW_MARGIN
+ 1;
2633 struct recalc_span_closure
{
2639 cb_recalc_spans_in_col (GnmColRowIter
const *iter
, gpointer user
)
2641 struct recalc_span_closure
*closure
= user
;
2642 int const col
= closure
->col
;
2644 CellSpanInfo
const *span
= row_span_get (iter
->cri
, col
);
2647 /* If there is an existing span see if it changed */
2648 GnmCell
const * const cell
= span
->cell
;
2649 cell_calc_span (cell
, &left
, &right
);
2650 if (left
!= span
->left
|| right
!= span
->right
) {
2651 cell_unregister_span (cell
);
2652 cell_register_span (cell
, left
, right
);
2655 /* If there is a cell see if it started to span */
2656 GnmCell
const * const cell
= sheet_cell_get (closure
->sheet
, col
, iter
->pos
);
2658 cell_calc_span (cell
, &left
, &right
);
2660 cell_register_span (cell
, left
, right
);
2668 * sheet_recompute_spans_for_col:
2670 * @col: The column that changed
2672 * This routine recomputes the column span for the cells that touches
2676 sheet_recompute_spans_for_col (Sheet
*sheet
, int col
)
2678 struct recalc_span_closure closure
;
2679 closure
.sheet
= sheet
;
2682 sheet_colrow_foreach (sheet
, FALSE
, 0, -1,
2683 &cb_recalc_spans_in_col
, &closure
);
2686 /****************************************************************************/
2689 GnmExprTop
const *texpr
;
2690 GnmRange expr_bound
;
2691 } closure_set_cell_value
;
2694 cb_set_cell_content (GnmCellIter
const *iter
, closure_set_cell_value
*info
)
2696 GnmExprTop
const *texpr
= info
->texpr
;
2701 cell
= sheet_cell_create (iter
->pp
.sheet
,
2706 * If we are overwriting an array, we need to clear things here
2707 * or gnm_cell_set_expr/gnm_cell_set_value will complain.
2709 if (cell
->base
.texpr
&& gnm_expr_top_is_array (cell
->base
.texpr
))
2710 gnm_cell_cleanout (cell
);
2712 if (texpr
!= NULL
) {
2713 if (!range_contains (&info
->expr_bound
,
2714 iter
->pp
.eval
.col
, iter
->pp
.eval
.row
)) {
2715 GnmExprRelocateInfo rinfo
;
2717 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
2718 rinfo
.pos
= iter
->pp
;
2719 rinfo
.origin
.start
= iter
->pp
.eval
;
2720 rinfo
.origin
.end
= iter
->pp
.eval
;
2721 rinfo
.origin_sheet
= iter
->pp
.sheet
;
2722 rinfo
.target_sheet
= iter
->pp
.sheet
;
2723 rinfo
.col_offset
= 0;
2724 rinfo
.row_offset
= 0;
2725 texpr
= gnm_expr_top_relocate (texpr
, &rinfo
, FALSE
);
2728 gnm_cell_set_expr (cell
, texpr
);
2730 gnm_cell_set_value (cell
, value_dup (info
->val
));
2735 cb_clear_non_corner (GnmCellIter
const *iter
, GnmRange
const *merged
)
2737 if (merged
->start
.col
!= iter
->pp
.eval
.col
||
2738 merged
->start
.row
!= iter
->pp
.eval
.row
)
2739 gnm_cell_set_value (iter
->cell
, value_new_empty ());
2744 * sheet_range_set_expr_cb:
2745 * @sr: #GnmSheetRange
2746 * @texpr: #GnmExprTop
2749 * Does NOT check for array division.
2752 sheet_range_set_expr_cb (GnmSheetRange
const *sr
, GnmExprTop
const *texpr
)
2754 closure_set_cell_value closure
;
2755 GSList
*merged
, *ptr
;
2757 g_return_if_fail (sr
!= NULL
);
2758 g_return_if_fail (texpr
!= NULL
);
2760 closure
.texpr
= texpr
;
2761 gnm_expr_top_get_boundingbox (closure
.texpr
,
2763 &closure
.expr_bound
);
2765 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2766 /* Store the parsed result creating any cells necessary */
2767 sheet_foreach_cell_in_range
2768 (sr
->sheet
, CELL_ITER_ALL
, &sr
->range
,
2769 (CellIterFunc
)&cb_set_cell_content
, &closure
);
2771 merged
= gnm_sheet_merge_get_overlap (sr
->sheet
, &sr
->range
);
2772 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
2773 GnmRange
const *tmp
= ptr
->data
;
2774 sheet_foreach_cell_in_range
2775 (sr
->sheet
, CELL_ITER_IGNORE_BLANK
, tmp
,
2776 (CellIterFunc
)&cb_clear_non_corner
,
2779 g_slist_free (merged
);
2781 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2782 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2783 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2788 * sheet_range_set_expr_undo:
2789 * @sr: (transfer full): #GnmSheetRange
2790 * @texpr: (transfer none): #GnmExprTop
2792 * Returns: (transfer full): the newly created #GOUndo.
2795 sheet_range_set_expr_undo (GnmSheetRange
*sr
, GnmExprTop
const *texpr
)
2797 gnm_expr_top_ref (texpr
);
2798 return go_undo_binary_new
2799 (sr
, (gpointer
)texpr
,
2800 (GOUndoBinaryFunc
) sheet_range_set_expr_cb
,
2801 (GFreeFunc
) gnm_sheet_range_free
,
2802 (GFreeFunc
) gnm_expr_top_unref
);
2807 * sheet_range_set_text:
2808 * @pos: The position from which to parse an expression.
2809 * @r: The range to fill
2810 * @str: The text to be parsed and assigned.
2812 * Does NOT check for array division.
2814 * Does NOT generate spans.
2817 sheet_range_set_text (GnmParsePos
const *pos
, GnmRange
const *r
, char const *str
)
2819 closure_set_cell_value closure
;
2820 GSList
*merged
, *ptr
;
2823 g_return_if_fail (pos
!= NULL
);
2824 g_return_if_fail (r
!= NULL
);
2825 g_return_if_fail (str
!= NULL
);
2829 parse_text_value_or_expr (pos
, str
,
2830 &closure
.val
, &closure
.texpr
);
2833 gnm_expr_top_get_boundingbox (closure
.texpr
,
2835 &closure
.expr_bound
);
2837 /* Store the parsed result creating any cells necessary */
2838 sheet_foreach_cell_in_range (sheet
, CELL_ITER_ALL
, r
,
2839 (CellIterFunc
)&cb_set_cell_content
, &closure
);
2841 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
2842 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
2843 GnmRange
const *tmp
= ptr
->data
;
2844 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
, tmp
,
2845 (CellIterFunc
)&cb_clear_non_corner
, (gpointer
)tmp
);
2847 g_slist_free (merged
);
2849 sheet_region_queue_recalc (sheet
, r
);
2851 value_release (closure
.val
);
2853 gnm_expr_top_unref (closure
.texpr
);
2855 sheet_flag_status_update_range (sheet
, r
);
2859 sheet_range_set_text_cb (GnmSheetRange
const *sr
, gchar
const *text
)
2863 pos
.eval
= sr
->range
.start
;
2864 pos
.sheet
= sr
->sheet
;
2865 pos
.wb
= sr
->sheet
->workbook
;
2867 sheet_range_set_text (&pos
, &sr
->range
, text
);
2868 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2869 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2870 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2872 sheet_redraw_range (sr
->sheet
, &sr
->range
);
2876 * sheet_range_set_text_undo:
2877 * @sr: (transfer full): #GnmSheetRange
2878 * @text: (transfer none): text for range
2880 * Returns: (transfer full): the newly created #GOUndo.
2883 sheet_range_set_text_undo (GnmSheetRange
*sr
,
2886 return go_undo_binary_new
2887 (sr
, g_strdup (text
),
2888 (GOUndoBinaryFunc
) sheet_range_set_text_cb
,
2889 (GFreeFunc
) gnm_sheet_range_free
,
2890 (GFreeFunc
) g_free
);
2895 cb_set_markup (GnmCellIter
const *iter
, PangoAttrList
*markup
)
2903 if (VALUE_IS_STRING (cell
->value
)) {
2905 GnmValue
*val
= value_dup (cell
->value
);
2907 fmt
= go_format_new_markup (markup
, TRUE
);
2908 value_set_fmt (val
, fmt
);
2909 go_format_unref (fmt
);
2911 gnm_cell_cleanout (cell
);
2912 gnm_cell_assign_value (cell
, val
);
2918 sheet_range_set_markup_cb (GnmSheetRange
const *sr
, PangoAttrList
*markup
)
2920 sheet_foreach_cell_in_range
2921 (sr
->sheet
, CELL_ITER_ALL
, &sr
->range
,
2922 (CellIterFunc
)&cb_set_markup
, markup
);
2924 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2925 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2926 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2931 * sheet_range_set_markup_undo:
2932 * @sr: (transfer full): #GnmSheetRange
2933 * @markup: (transfer none) (nullable): #PangoAttrList
2935 * Returns: (transfer full) (nullable): the newly created #GOUndo.
2938 sheet_range_set_markup_undo (GnmSheetRange
*sr
, PangoAttrList
*markup
)
2942 return go_undo_binary_new
2943 (sr
, pango_attr_list_ref (markup
),
2944 (GOUndoBinaryFunc
) sheet_range_set_markup_cb
,
2945 (GFreeFunc
) gnm_sheet_range_free
,
2946 (GFreeFunc
) pango_attr_list_unref
);
2950 * sheet_cell_get_value:
2952 * @col: Source column
2955 * Returns: (transfer none) (nullable): the cell's current value. The return
2956 * value will be %NULL only when the cell does not exist.
2959 sheet_cell_get_value (Sheet
*sheet
, int const col
, int const row
)
2963 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2965 cell
= sheet_cell_get (sheet
, col
, row
);
2967 return cell
? cell
->value
: NULL
;
2971 * sheet_cell_set_text:
2973 * @str: the text to set.
2974 * @markup: (allow-none): an optional PangoAttrList.
2976 * Marks the sheet as dirty
2978 * Flags status updates
2982 sheet_cell_set_text (GnmCell
*cell
, char const *text
, PangoAttrList
*markup
)
2984 GnmExprTop
const *texpr
;
2988 g_return_if_fail (cell
!= NULL
);
2989 g_return_if_fail (text
!= NULL
);
2990 g_return_if_fail (!gnm_cell_is_nonsingleton_array (cell
));
2992 parse_text_value_or_expr (parse_pos_init_cell (&pp
, cell
),
2993 text
, &val
, &texpr
);
2995 /* Queue a redraw before in case the span changes */
2996 sheet_redraw_cell (cell
);
2998 if (texpr
!= NULL
) {
2999 gnm_cell_set_expr (cell
, texpr
);
3000 gnm_expr_top_unref (texpr
);
3003 * Queue recalc before spanning. Otherwise spanning may
3004 * create a bogus rendered value, see #495879.
3006 cell_queue_recalc (cell
);
3008 /* Clear spans from _other_ cells */
3009 sheet_cell_calc_span (cell
, GNM_SPANCALC_SIMPLE
);
3011 g_return_if_fail (val
!= NULL
);
3013 if (markup
!= NULL
&& VALUE_IS_STRING (val
)) {
3014 gboolean quoted
= (text
[0] == '\'');
3015 PangoAttrList
*adj_markup
;
3019 /* We ate the quote. Adjust. Ugh. */
3020 adj_markup
= pango_attr_list_copy (markup
);
3021 go_pango_attr_list_erase (adj_markup
, 0, 1);
3023 adj_markup
= markup
;
3025 fmt
= go_format_new_markup (adj_markup
, TRUE
);
3026 value_set_fmt (val
, fmt
);
3027 go_format_unref (fmt
);
3029 pango_attr_list_unref (adj_markup
);
3032 gnm_cell_set_value (cell
, val
);
3034 /* Queue recalc before spanning, see above. */
3035 cell_queue_recalc (cell
);
3037 sheet_cell_calc_span (cell
, GNM_SPANCALC_RESIZE
| GNM_SPANCALC_RENDER
);
3040 sheet_flag_status_update_cell (cell
);
3044 * sheet_cell_set_text_gi: (rename-to sheet_cell_set_text)
3046 * @col: column number
3048 * @str: the text to set.
3050 * Sets the contents of a cell.
3053 sheet_cell_set_text_gi (Sheet
*sheet
, int col
, int row
, char const *str
)
3055 sheet_cell_set_text (sheet_cell_fetch (sheet
, col
, row
), str
, NULL
);
3060 * sheet_cell_set_expr:
3062 * @texpr: New expression for @cell.
3064 * Marks the sheet as dirty
3066 * Flags status updates
3070 sheet_cell_set_expr (GnmCell
*cell
, GnmExprTop
const *texpr
)
3072 gnm_cell_set_expr (cell
, texpr
);
3074 /* clear spans from _other_ cells */
3075 sheet_cell_calc_span (cell
, GNM_SPANCALC_SIMPLE
);
3077 cell_queue_recalc (cell
);
3078 sheet_flag_status_update_cell (cell
);
3082 * sheet_cell_set_value: (skip)
3084 * @v: (transfer full): #GnmValue
3086 * Stores, without copying, the supplied value. It marks the
3089 * The value is rendered and spans are calculated. It queues a redraw
3090 * and checks to see if the edit region or selection content changed.
3092 * NOTE : This DOES check for array partitioning.
3095 sheet_cell_set_value (GnmCell
*cell
, GnmValue
*v
)
3097 /* TODO : if the value is unchanged do not assign it */
3098 gnm_cell_set_value (cell
, v
);
3099 sheet_cell_calc_span (cell
, GNM_SPANCALC_RESIZE
| GNM_SPANCALC_RENDER
);
3100 cell_queue_recalc (cell
);
3101 sheet_flag_status_update_cell (cell
);
3105 * sheet_cell_set_value_gi: (rename-to sheet_cell_set_value)
3107 * @col: column number
3111 * Set the the value of the cell at (@col,@row) to @v.
3113 * The value is rendered and spans are calculated. It queues a redraw
3114 * and checks to see if the edit region or selection content changed.
3117 sheet_cell_set_value_gi (Sheet
*sheet
, int col
, int row
, GnmValue
*v
)
3119 // This version exists because not all versions of pygobject
3120 // understand transfer-full parameters
3121 sheet_cell_set_value (sheet_cell_fetch (sheet
, col
, row
),
3125 /****************************************************************************/
3128 * This routine is used to queue the redraw regions for the
3129 * cell region specified.
3131 * It is usually called before a change happens to a region,
3132 * and after the change has been done to queue the regions
3133 * for the old contents and the new contents.
3135 * It intelligently handles spans and merged ranges
3138 sheet_range_bounding_box (Sheet
const *sheet
, GnmRange
*bound
)
3142 GnmRange r
= *bound
;
3144 g_return_if_fail (range_is_sane (bound
));
3146 /* Check the first and last columns for spans and extend the region to
3147 * include the maximum extent.
3149 for (row
= r
.start
.row
; row
<= r
.end
.row
; row
++){
3150 ColRowInfo
const *ri
= sheet_row_get (sheet
, row
);
3153 CellSpanInfo
const * span0
;
3155 if (ri
->needs_respan
)
3156 row_calc_spans ((ColRowInfo
*)ri
, row
, sheet
);
3158 span0
= row_span_get (ri
, r
.start
.col
);
3160 if (span0
!= NULL
) {
3161 if (bound
->start
.col
> span0
->left
)
3162 bound
->start
.col
= span0
->left
;
3163 if (bound
->end
.col
< span0
->right
)
3164 bound
->end
.col
= span0
->right
;
3166 if (r
.start
.col
!= r
.end
.col
) {
3167 CellSpanInfo
const * span1
=
3168 row_span_get (ri
, r
.end
.col
);
3170 if (span1
!= NULL
) {
3171 if (bound
->start
.col
> span1
->left
)
3172 bound
->start
.col
= span1
->left
;
3173 if (bound
->end
.col
< span1
->right
)
3174 bound
->end
.col
= span1
->right
;
3177 /* skip segments with no cells */
3178 } else if (row
== COLROW_SEGMENT_START (row
)) {
3179 ColRowSegment
const * const segment
=
3180 COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
3181 if (segment
== NULL
)
3182 row
= COLROW_SEGMENT_END (row
);
3186 /* TODO : this may get expensive if there are alot of merged ranges */
3187 /* no need to iterate, one pass is enough */
3188 for (ptr
= sheet
->list_merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3189 GnmRange
const * const test
= ptr
->data
;
3190 if (r
.start
.row
<= test
->end
.row
|| r
.end
.row
>= test
->start
.row
) {
3191 if (bound
->start
.col
> test
->start
.col
)
3192 bound
->start
.col
= test
->start
.col
;
3193 if (bound
->end
.col
< test
->end
.col
)
3194 bound
->end
.col
= test
->end
.col
;
3195 if (bound
->start
.row
> test
->start
.row
)
3196 bound
->start
.row
= test
->start
.row
;
3197 if (bound
->end
.row
< test
->end
.row
)
3198 bound
->end
.row
= test
->end
.row
;
3204 sheet_redraw_region (Sheet
const *sheet
,
3205 int start_col
, int start_row
,
3206 int end_col
, int end_row
)
3210 g_return_if_fail (IS_SHEET (sheet
));
3213 * Getting the bounding box causes row respans to be done if
3214 * needed. That can be expensive, so just redraw the whole
3215 * sheet if the row count is too big.
3217 if (end_row
- start_row
> 500) {
3218 sheet_redraw_all (sheet
, FALSE
);
3222 /* We potentially do a lot of recalcs as part of this, so make sure
3223 stuff that caches sub-computations see the whole thing instead
3224 of clearing between cells. */
3225 gnm_app_recalc_start ();
3227 sheet_range_bounding_box (sheet
,
3228 range_init (&bound
, start_col
, start_row
, end_col
, end_row
));
3229 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
3230 sc_redraw_range (control
, &bound
););
3232 gnm_app_recalc_finish ();
3236 sheet_redraw_range (Sheet
const *sheet
, GnmRange
const *range
)
3238 g_return_if_fail (IS_SHEET (sheet
));
3239 g_return_if_fail (range
!= NULL
);
3241 sheet_redraw_region (sheet
,
3242 range
->start
.col
, range
->start
.row
,
3243 range
->end
.col
, range
->end
.row
);
3246 /****************************************************************************/
3249 sheet_col_is_hidden (Sheet
const *sheet
, int col
)
3251 ColRowInfo
const * const res
= sheet_col_get (sheet
, col
);
3252 return (res
!= NULL
&& !res
->visible
);
3256 sheet_row_is_hidden (Sheet
const *sheet
, int row
)
3258 ColRowInfo
const * const res
= sheet_row_get (sheet
, row
);
3259 return (res
!= NULL
&& !res
->visible
);
3264 * sheet_find_boundary_horizontal:
3266 * @col: The column from which to begin searching.
3267 * @move_row: The row in which to search for the edge of the range.
3268 * @base_row: The height of the area being moved.
3269 * @count: units to extend the selection vertically
3270 * @jump_to_boundaries: Jump to range boundaries.
3272 * Calculate the column index for the column which is @count units
3273 * from @start_col doing bounds checking. If @jump_to_boundaries is
3274 * %TRUE then @count must be 1 and the jump is to the edge of the logical range.
3276 * This routine implements the logic necasary for ctrl-arrow style
3277 * movement. That is more complicated than simply finding the last in a list
3278 * of cells with content. If you are at the end of a range it will find the
3279 * start of the next. Make sure that is the sort of behavior you want before
3282 * Returns: the column index.
3285 sheet_find_boundary_horizontal (Sheet
*sheet
, int start_col
, int move_row
,
3286 int base_row
, int count
,
3287 gboolean jump_to_boundaries
)
3289 gboolean find_nonblank
= sheet_is_cell_empty (sheet
, start_col
, move_row
);
3290 gboolean keep_looking
= FALSE
;
3291 int new_col
, prev_col
, lagged_start_col
, max_col
= gnm_sheet_get_last_col (sheet
);
3293 GnmRange check_merge
;
3294 GnmRange
const * const bound
= &sheet
->priv
->unhidden_region
;
3296 /* Jumping to bounds requires steping cell by cell */
3297 g_return_val_if_fail (count
== 1 || count
== -1 || !jump_to_boundaries
, start_col
);
3298 g_return_val_if_fail (IS_SHEET (sheet
), start_col
);
3300 if (move_row
< base_row
) {
3301 check_merge
.start
.row
= move_row
;
3302 check_merge
.end
.row
= base_row
;
3304 check_merge
.end
.row
= move_row
;
3305 check_merge
.start
.row
= base_row
;
3309 GSList
*merged
, *ptr
;
3311 lagged_start_col
= check_merge
.start
.col
= check_merge
.end
.col
= start_col
;
3312 merged
= gnm_sheet_merge_get_overlap (sheet
, &check_merge
);
3313 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3314 GnmRange
const * const r
= ptr
->data
;
3316 if (start_col
< r
->end
.col
)
3317 start_col
= r
->end
.col
;
3319 if (start_col
> r
->start
.col
)
3320 start_col
= r
->start
.col
;
3323 g_slist_free (merged
);
3324 } while (start_col
!= lagged_start_col
);
3325 new_col
= prev_col
= start_col
;
3331 if (new_col
< bound
->start
.col
)
3332 return MIN (bound
->start
.col
, max_col
);
3333 if (new_col
> bound
->end
.col
)
3334 return MIN (bound
->end
.col
, max_col
);
3336 keep_looking
= sheet_col_is_hidden (sheet
, new_col
);
3337 if (jump_to_boundaries
) {
3338 if (new_col
> sheet
->cols
.max_used
) {
3340 return (find_nonblank
|| iterations
== 1)?
3341 MIN (bound
->end
.col
, max_col
):
3342 MIN (prev_col
, max_col
);
3343 new_col
= sheet
->cols
.max_used
;
3346 keep_looking
|= (sheet_is_cell_empty (sheet
, new_col
, move_row
) == find_nonblank
);
3349 else if (!find_nonblank
) {
3351 * Handle special case where we are on the last
3354 if (iterations
== 1)
3355 keep_looking
= find_nonblank
= TRUE
;
3360 } while (keep_looking
);
3362 return MIN (new_col
, max_col
);
3366 * sheet_find_boundary_vertical:
3368 * @move_col: The col in which to search for the edge of the range.
3369 * @row: The row from which to begin searching.
3370 * @base_col: The width of the area being moved.
3371 * @count: units to extend the selection vertically
3372 * @jump_to_boundaries: Jump to range boundaries.
3374 * Calculate the row index for the row which is @count units
3375 * from @start_row doing bounds checking. If @jump_to_boundaries is
3376 * %TRUE then @count must be 1 and the jump is to the edge of the logical range.
3378 * This routine implements the logic necasary for ctrl-arrow style
3379 * movement. That is more complicated than simply finding the last in a list
3380 * of cells with content. If you are at the end of a range it will find the
3381 * start of the next. Make sure that is the sort of behavior you want before
3384 * Returns: the row index.
3387 sheet_find_boundary_vertical (Sheet
*sheet
, int move_col
, int start_row
,
3388 int base_col
, int count
,
3389 gboolean jump_to_boundaries
)
3391 gboolean find_nonblank
= sheet_is_cell_empty (sheet
, move_col
, start_row
);
3392 gboolean keep_looking
= FALSE
;
3393 int new_row
, prev_row
, lagged_start_row
, max_row
= gnm_sheet_get_last_row (sheet
);
3395 GnmRange check_merge
;
3396 GnmRange
const * const bound
= &sheet
->priv
->unhidden_region
;
3398 /* Jumping to bounds requires steping cell by cell */
3399 g_return_val_if_fail (count
== 1 || count
== -1 || !jump_to_boundaries
, start_row
);
3400 g_return_val_if_fail (IS_SHEET (sheet
), start_row
);
3402 if (move_col
< base_col
) {
3403 check_merge
.start
.col
= move_col
;
3404 check_merge
.end
.col
= base_col
;
3406 check_merge
.end
.col
= move_col
;
3407 check_merge
.start
.col
= base_col
;
3411 GSList
*merged
, *ptr
;
3413 lagged_start_row
= check_merge
.start
.row
= check_merge
.end
.row
= start_row
;
3414 merged
= gnm_sheet_merge_get_overlap (sheet
, &check_merge
);
3415 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3416 GnmRange
const * const r
= ptr
->data
;
3418 if (start_row
< r
->end
.row
)
3419 start_row
= r
->end
.row
;
3421 if (start_row
> r
->start
.row
)
3422 start_row
= r
->start
.row
;
3425 g_slist_free (merged
);
3426 } while (start_row
!= lagged_start_row
);
3427 new_row
= prev_row
= start_row
;
3433 if (new_row
< bound
->start
.row
)
3434 return MIN (bound
->start
.row
, max_row
);
3435 if (new_row
> bound
->end
.row
)
3436 return MIN (bound
->end
.row
, max_row
);
3438 keep_looking
= sheet_row_is_hidden (sheet
, new_row
);
3439 if (jump_to_boundaries
) {
3440 if (new_row
> sheet
->rows
.max_used
) {
3442 return (find_nonblank
|| iterations
== 1)?
3443 MIN (bound
->end
.row
, max_row
):
3444 MIN (prev_row
, max_row
);
3445 new_row
= sheet
->rows
.max_used
;
3448 keep_looking
|= (sheet_is_cell_empty (sheet
, move_col
, new_row
) == find_nonblank
);
3451 else if (!find_nonblank
) {
3453 * Handle special case where we are on the last
3456 if (iterations
== 1)
3457 keep_looking
= find_nonblank
= TRUE
;
3462 } while (keep_looking
);
3464 return MIN (new_row
, max_row
);
3468 CHECK_AND_LOAD_START
= 1,
3477 GnmRange
const *ignore
;
3483 cb_check_array_horizontal (GnmColRowIter
const *iter
, gpointer data_
)
3485 ArrayCheckData
*data
= data_
;
3486 gboolean is_array
= FALSE
;
3488 if (data
->flags
& CHECK_AND_LOAD_START
&& /* Top */
3489 (is_array
= gnm_cell_array_bound (
3490 sheet_cell_get (data
->sheet
, iter
->pos
, data
->start
),
3492 data
->error
.start
.row
< data
->start
&&
3493 (data
->ignore
== NULL
||
3494 !range_contained (&data
->error
, data
->ignore
)))
3497 if (data
->flags
& LOAD_END
)
3498 is_array
= gnm_cell_array_bound (
3499 sheet_cell_get (data
->sheet
, iter
->pos
, data
->end
),
3502 return (data
->flags
& CHECK_END
&&
3504 data
->error
.end
.row
> data
->end
&& /* Bottom */
3505 (data
->ignore
== NULL
||
3506 !range_contained (&data
->error
, data
->ignore
)));
3510 cb_check_array_vertical (GnmColRowIter
const *iter
, gpointer data_
)
3512 ArrayCheckData
*data
= data_
;
3513 gboolean is_array
= FALSE
;
3515 if (data
->flags
& CHECK_AND_LOAD_START
&& /* Left */
3516 (is_array
= gnm_cell_array_bound (
3517 sheet_cell_get (data
->sheet
, data
->start
, iter
->pos
),
3519 data
->error
.start
.col
< data
->start
&&
3520 (data
->ignore
== NULL
||
3521 !range_contained (&data
->error
, data
->ignore
)))
3524 if (data
->flags
& LOAD_END
)
3525 is_array
= gnm_cell_array_bound (
3526 sheet_cell_get (data
->sheet
, data
->end
, iter
->pos
),
3529 return (data
->flags
& CHECK_END
&&
3531 data
->error
.end
.col
> data
->end
&& /* Right */
3532 (data
->ignore
== NULL
||
3533 !range_contained (&data
->error
, data
->ignore
)));
3537 * sheet_range_splits_array:
3538 * @sheet: The sheet.
3539 * @r: The range to check
3540 * @ignore: (nullable): a range in which it is ok to have an array.
3541 * @cc: (nullable): place to report an error.
3542 * @cmd: (nullable): cmd name used with @cc.
3544 * Check the outer edges of range @sheet!@r to ensure that if an array is
3545 * within it then the entire array is within the range. @ignore is useful when
3546 * src and dest ranges may overlap.
3548 * Returns: %TRUE if an array would be split.
3551 sheet_range_splits_array (Sheet
const *sheet
,
3552 GnmRange
const *r
, GnmRange
const *ignore
,
3553 GOCmdContext
*cc
, char const *cmd
)
3555 ArrayCheckData closure
;
3557 g_return_val_if_fail (r
->start
.col
<= r
->end
.col
, FALSE
);
3558 g_return_val_if_fail (r
->start
.row
<= r
->end
.row
, FALSE
);
3560 closure
.sheet
= sheet
;
3561 closure
.ignore
= ignore
;
3563 closure
.start
= r
->start
.row
;
3564 closure
.end
= r
->end
.row
;
3565 if (closure
.start
<= 0) {
3566 closure
.flags
= (closure
.end
< sheet
->rows
.max_used
)
3567 ? CHECK_END
| LOAD_END
3569 } else if (closure
.end
< sheet
->rows
.max_used
)
3570 closure
.flags
= (closure
.start
== closure
.end
)
3571 ? CHECK_AND_LOAD_START
| CHECK_END
3572 : CHECK_AND_LOAD_START
| CHECK_END
| LOAD_END
;
3574 closure
.flags
= CHECK_AND_LOAD_START
;
3576 if (closure
.flags
&&
3577 sheet_colrow_foreach (sheet
, TRUE
,
3578 r
->start
.col
, r
->end
.col
,
3579 cb_check_array_horizontal
, &closure
)) {
3581 gnm_cmd_context_error_splits_array (cc
,
3582 cmd
, &closure
.error
);
3586 closure
.start
= r
->start
.col
;
3587 closure
.end
= r
->end
.col
;
3588 if (closure
.start
<= 0) {
3589 closure
.flags
= (closure
.end
< sheet
->cols
.max_used
)
3590 ? CHECK_END
| LOAD_END
3592 } else if (closure
.end
< sheet
->cols
.max_used
)
3593 closure
.flags
= (closure
.start
== closure
.end
)
3594 ? CHECK_AND_LOAD_START
| CHECK_END
3595 : CHECK_AND_LOAD_START
| CHECK_END
| LOAD_END
;
3597 closure
.flags
= CHECK_AND_LOAD_START
;
3599 if (closure
.flags
&&
3600 sheet_colrow_foreach (sheet
, FALSE
,
3601 r
->start
.row
, r
->end
.row
,
3602 cb_check_array_vertical
, &closure
)) {
3604 gnm_cmd_context_error_splits_array (cc
,
3605 cmd
, &closure
.error
);
3612 * sheet_range_splits_region:
3613 * @sheet: the sheet.
3614 * @r: The range whose boundaries are checked
3615 * @ignore: An optional range in which it is ok to have arrays and merges
3616 * @cc: The context that issued the command
3617 * @cmd: The translated command name.
3619 * A utility to see whether moving the range @r will split any arrays
3620 * or merged regions.
3621 * Returns: whether any arrays or merged regions will be split.
3624 sheet_range_splits_region (Sheet
const *sheet
,
3625 GnmRange
const *r
, GnmRange
const *ignore
,
3626 GOCmdContext
*cc
, char const *cmd_name
)
3630 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
3632 /* Check for array subdivision */
3633 if (sheet_range_splits_array (sheet
, r
, ignore
, cc
, cmd_name
))
3636 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
3640 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3641 GnmRange
const *m
= ptr
->data
;
3642 if (ignore
!= NULL
&& range_contained (m
, ignore
))
3644 if (!range_contained (m
, r
))
3647 g_slist_free (merged
);
3649 if (cc
!= NULL
&& ptr
!= NULL
) {
3650 go_cmd_context_error_invalid (cc
, cmd_name
,
3651 _("Target region contains merged cells"));
3659 * sheet_ranges_split_region:
3660 * @sheet: the sheet.
3661 * @ranges: (element-type GnmRange): A list of ranges to check.
3662 * @cc: The context that issued the command
3663 * @cmd: The translated command name.
3665 * A utility to see whether moving any of the ranges @ranges will split any
3666 * arrays or merged regions.
3667 * Returns: whether any arrays or merged regions will be splitted.
3670 sheet_ranges_split_region (Sheet
const * sheet
, GSList
const *ranges
,
3671 GOCmdContext
*cc
, char const *cmd
)
3675 /* Check for array subdivision */
3676 for (l
= ranges
; l
!= NULL
; l
= l
->next
) {
3677 GnmRange
const *r
= l
->data
;
3678 if (sheet_range_splits_region (sheet
, r
, NULL
, cc
, cmd
))
3685 cb_cell_is_array (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
3687 return gnm_cell_is_array (iter
->cell
) ? VALUE_TERMINATE
: NULL
;
3691 * sheet_range_contains_merges_or_arrays:
3693 * @r: the range to check.
3694 * @cc: an optional place to report errors.
3696 * @merges: if %TRUE, check for merges.
3697 * @arrays: if %TRUE, check for arrays.
3699 * Check to see if the target region @sheet!@r contains any merged regions or
3700 * arrays. Report an error to the @cc if it is supplied.
3701 * Returns: %TRUE if the target region @sheet!@r contains any merged regions or
3705 sheet_range_contains_merges_or_arrays (Sheet
const *sheet
, GnmRange
const *r
,
3706 GOCmdContext
*cc
, char const *cmd
,
3707 gboolean merges
, gboolean arrays
)
3709 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
3712 GSList
*merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
3713 if (merged
!= NULL
) {
3715 go_cmd_context_error_invalid
3717 _("cannot operate on merged cells"));
3718 g_slist_free (merged
);
3724 if (sheet_foreach_cell_in_range (
3725 (Sheet
*)sheet
, CELL_ITER_IGNORE_NONEXISTENT
, r
,
3726 cb_cell_is_array
, NULL
)) {
3728 go_cmd_context_error_invalid
3730 _("cannot operate on array formul\303\246"));
3738 /***************************************************************************/
3741 * sheet_colrow_get_default:
3743 * @is_cols: %TRUE for columns, %FALSE for rows.
3745 * Returns: (transfer none): the default #ColRowInfo.
3748 sheet_colrow_get_default (Sheet
const *sheet
, gboolean is_cols
)
3750 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3752 return is_cols
? &sheet
->cols
.default_style
: &sheet
->rows
.default_style
;
3756 sheet_colrow_optimize1 (int max
, int max_used
, ColRowCollection
*collection
)
3759 int first_unused
= max_used
+ 1;
3761 for (i
= COLROW_SEGMENT_START (first_unused
);
3763 i
+= COLROW_SEGMENT_SIZE
) {
3764 ColRowSegment
*segment
= COLROW_GET_SEGMENT (collection
, i
);
3766 gboolean any
= FALSE
;
3770 for (j
= 0; j
< COLROW_SEGMENT_SIZE
; j
++) {
3771 ColRowInfo
*info
= segment
->info
[j
];
3774 if (i
+ j
>= first_unused
&&
3775 col_row_info_equal (&collection
->default_style
, info
)) {
3777 segment
->info
[j
] = NULL
;
3780 if (i
+ j
>= first_unused
)
3787 COLROW_GET_SEGMENT (collection
, i
) = NULL
;
3791 collection
->max_used
= max_used
;
3795 sheet_colrow_optimize (Sheet
*sheet
)
3799 g_return_if_fail (IS_SHEET (sheet
));
3801 extent
= sheet_get_cells_extent (sheet
);
3803 sheet_colrow_optimize1 (gnm_sheet_get_max_cols (sheet
),
3806 sheet_colrow_optimize1 (gnm_sheet_get_max_rows (sheet
),
3813 * @col: column number
3815 * Returns: (transfer none) (nullable): A #ColRowInfo for the column, or %NULL
3816 * if none has been allocated yet.
3819 sheet_col_get (Sheet
const *sheet
, int col
)
3821 ColRowSegment
*segment
;
3823 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3824 g_return_val_if_fail (col
< gnm_sheet_get_max_cols (sheet
), NULL
);
3825 g_return_val_if_fail (col
>= 0, NULL
);
3827 segment
= COLROW_GET_SEGMENT (&(sheet
->cols
), col
);
3828 if (segment
!= NULL
)
3829 return segment
->info
[COLROW_SUB_INDEX (col
)];
3837 * Returns: (transfer none) (nullable): A #ColRowInfo for the row, or %NULL
3838 * if none has been allocated yet.
3841 sheet_row_get (Sheet
const *sheet
, int row
)
3843 ColRowSegment
*segment
;
3845 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3846 g_return_val_if_fail (row
< gnm_sheet_get_max_rows (sheet
), NULL
);
3847 g_return_val_if_fail (row
>= 0, NULL
);
3849 segment
= COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
3850 if (segment
!= NULL
)
3851 return segment
->info
[COLROW_SUB_INDEX (row
)];
3856 sheet_colrow_get (Sheet
const *sheet
, int colrow
, gboolean is_cols
)
3859 return sheet_col_get (sheet
, colrow
);
3860 return sheet_row_get (sheet
, colrow
);
3865 * @col: column number
3867 * Returns: (transfer none): The #ColRowInfo for column @col. This result
3868 * will not be the default #ColRowInfo and may be changed.
3871 sheet_col_fetch (Sheet
*sheet
, int pos
)
3873 ColRowInfo
*cri
= sheet_col_get (sheet
, pos
);
3874 if (NULL
== cri
&& NULL
!= (cri
= sheet_col_new (sheet
)))
3875 sheet_colrow_add (sheet
, cri
, TRUE
, pos
);
3883 * Returns: (transfer none): The #ColRowInfo for row @row. This result
3884 * will not be the default #ColRowInfo and may be changed.
3887 sheet_row_fetch (Sheet
*sheet
, int pos
)
3889 ColRowInfo
*cri
= sheet_row_get (sheet
, pos
);
3890 if (NULL
== cri
&& NULL
!= (cri
= sheet_row_new (sheet
)))
3891 sheet_colrow_add (sheet
, cri
, FALSE
, pos
);
3896 sheet_colrow_fetch (Sheet
*sheet
, int colrow
, gboolean is_cols
)
3899 return sheet_col_fetch (sheet
, colrow
);
3900 return sheet_row_fetch (sheet
, colrow
);
3904 * sheet_col_get_info:
3905 * @col: column number
3907 * Returns: (transfer none): The #ColRowInfo for column @col. The may be
3908 * the default #ColRowInfo for columns and should not be changed.
3911 sheet_col_get_info (Sheet
const *sheet
, int col
)
3913 ColRowInfo
*ci
= sheet_col_get (sheet
, col
);
3917 return &sheet
->cols
.default_style
;
3921 * sheet_row_get_info:
3922 * @row: column number
3924 * Returns: (transfer none): The #ColRowInfo for row @row. The may be
3925 * the default #ColRowInfo for rows and should not be changed.
3928 sheet_row_get_info (Sheet
const *sheet
, int row
)
3930 ColRowInfo
*ri
= sheet_row_get (sheet
, row
);
3934 return &sheet
->rows
.default_style
;
3938 sheet_colrow_get_info (Sheet
const *sheet
, int colrow
, gboolean is_cols
)
3941 ? sheet_col_get_info (sheet
, colrow
)
3942 : sheet_row_get_info (sheet
, colrow
);
3946 * sheet_colrow_foreach:
3948 * @is_cols: %TRUE for columns, %FALSE for rows.
3949 * @first: start position (inclusive)
3950 * @last: stop position (inclusive), -1 meaning end-of-sheet
3951 * @callback: (scope call): A callback function which should return %TRUE
3952 * to stop the iteration.
3953 * @user_data: A baggage pointer.
3955 * Iterates through the existing rows or columns within the range supplied.
3956 * If a callback returns %TRUE, iteration stops.
3959 sheet_colrow_foreach (Sheet
const *sheet
,
3961 int first
, int last
,
3962 ColRowHandler callback
,
3965 ColRowCollection
const *infos
;
3967 ColRowSegment
const *segment
;
3968 int sub
, inner_last
, i
;
3970 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
3973 last
= colrow_max (is_cols
, sheet
) - 1;
3974 infos
= is_cols
? &sheet
->cols
: &sheet
->rows
;
3977 if (last
> infos
->max_used
)
3978 last
= infos
->max_used
;
3980 for (i
= first
; i
<= last
; ) {
3981 segment
= COLROW_GET_SEGMENT (infos
, i
);
3982 sub
= COLROW_SUB_INDEX(i
);
3983 inner_last
= (COLROW_SEGMENT_INDEX (last
) == COLROW_SEGMENT_INDEX (i
))
3984 ? COLROW_SUB_INDEX (last
)+1 : COLROW_SEGMENT_SIZE
;
3986 i
+= COLROW_SEGMENT_SIZE
- sub
;
3987 if (segment
== NULL
)
3990 for (; sub
< inner_last
; sub
++, iter
.pos
++) {
3991 iter
.cri
= segment
->info
[sub
];
3992 if (iter
.cri
!= NULL
&& (*callback
)(&iter
, user_data
))
4001 /*****************************************************************************/
4004 cell_ordering (gconstpointer a_
, gconstpointer b_
)
4006 GnmCell
const *a
= *(GnmCell
**)a_
;
4007 GnmCell
const *b
= *(GnmCell
**)b_
;
4009 if (a
->pos
.row
!= b
->pos
.row
)
4010 return a
->pos
.row
- b
->pos
.row
;
4012 return a
->pos
.col
- b
->pos
.col
;
4018 * @r: (nullable): a #GnmRange
4020 * Retrieves an array of all cells inside @r.
4021 * Returns: (element-type GnmCell) (transfer container): the cells array.
4024 sheet_cells (Sheet
*sheet
, const GnmRange
*r
)
4026 GPtrArray
*res
= g_ptr_array_new ();
4027 GHashTableIter hiter
;
4030 g_hash_table_iter_init (&hiter
, sheet
->cell_hash
);
4031 while (g_hash_table_iter_next (&hiter
, NULL
, &value
)) {
4032 GnmCell
*cell
= value
;
4033 if (!r
|| range_contains (r
, cell
->pos
.col
, cell
->pos
.row
))
4034 g_ptr_array_add (res
, cell
);
4036 g_ptr_array_sort (res
, cell_ordering
);
4043 #define SWAP_INT(a,b) do { int t; t = a; a = b; b = t; } while (0)
4046 * sheet_foreach_cell_in_range:
4050 * @callback: (scope call): #CellFilterFunc
4051 * @closure: user data.
4053 * For each existing cell in the range specified, invoke the
4054 * callback routine. If the only_existing flag is passed, then
4055 * callbacks are only invoked for existing cells.
4057 * Note: this function does not honour the CELL_ITER_IGNORE_SUBTOTAL flag.
4059 * Returns: (transfer none): the value returned by the callback, which can be:
4060 * non-%NULL on error, or VALUE_TERMINATE if some invoked routine requested
4061 * to stop (by returning non-%NULL).
4063 * NOTE: between 0.56 and 0.57, the traversal order changed. The order is now
4069 * (This appears to be the order in which XL looks at the values of ranges.)
4070 * If your code depends on any particular ordering, please add a very visible
4071 * comment near the call.
4074 sheet_foreach_cell_in_range (Sheet
*sheet
, CellIterFlags flags
,
4076 CellIterFunc callback
,
4079 return sheet_foreach_cell_in_region (sheet
, flags
,
4080 r
->start
.col
, r
->start
.row
,
4081 r
->end
.col
, r
->end
.row
,
4087 * sheet_foreach_cell_in_region:
4090 * @start_col: Starting column
4091 * @start_row: Starting row
4092 * @end_col: Ending column, -1 meaning last
4093 * @end_row: Ending row, -1 meaning last
4094 * @callback: (scope call): #CellFilterFunc
4095 * @closure: user data.
4097 * For each existing cell in the range specified, invoke the
4098 * callback routine. If the only_existing flag is passed, then
4099 * callbacks are only invoked for existing cells.
4101 * Note: this function does not honour the CELL_ITER_IGNORE_SUBTOTAL flag.
4103 * Returns: (transfer none): the value returned by the callback, which can be:
4104 * non-%NULL on error, or VALUE_TERMINATE if some invoked routine requested
4105 * to stop (by returning non-%NULL).
4107 * NOTE: between 0.56 and 0.57, the traversal order changed. The order is now
4113 * (This appears to be the order in which XL looks at the values of ranges.)
4114 * If your code depends on any particular ordering, please add a very visible
4115 * comment near the call.
4118 sheet_foreach_cell_in_region (Sheet
*sheet
, CellIterFlags flags
,
4119 int start_col
, int start_row
,
4120 int end_col
, int end_row
,
4121 CellIterFunc callback
, void *closure
)
4125 gboolean
const visibility_matters
= (flags
& CELL_ITER_IGNORE_HIDDEN
) != 0;
4126 gboolean
const ignore_filtered
= (flags
& CELL_ITER_IGNORE_FILTERED
) != 0;
4127 gboolean
const only_existing
= (flags
& CELL_ITER_IGNORE_NONEXISTENT
) != 0;
4128 gboolean
const ignore_empty
= (flags
& CELL_ITER_IGNORE_EMPTY
) != 0;
4130 gboolean use_celllist
;
4133 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
4134 g_return_val_if_fail (callback
!= NULL
, NULL
);
4137 if (end_col
== -1) end_col
= gnm_sheet_get_last_col (sheet
);
4138 if (end_row
== -1) end_row
= gnm_sheet_get_last_row (sheet
);
4140 iter
.pp
.sheet
= sheet
;
4141 iter
.pp
.wb
= sheet
->workbook
;
4143 if (start_col
> end_col
)
4144 SWAP_INT (start_col
, end_col
);
4145 if (end_col
< 0 || start_col
> gnm_sheet_get_last_col (sheet
))
4147 start_col
= MAX (0, start_col
);
4148 end_col
= MIN (end_col
, gnm_sheet_get_last_col (sheet
));
4150 if (start_row
> end_row
)
4151 SWAP_INT (start_row
, end_row
);
4152 if (end_row
< 0 || start_row
> gnm_sheet_get_last_row (sheet
))
4154 start_row
= MAX (0, start_row
);
4155 end_row
= MIN (end_row
, gnm_sheet_get_last_row (sheet
));
4157 range_size
= (guint64
)(end_row
- start_row
+ 1) * (end_col
- start_col
+ 1);
4160 range_size
> g_hash_table_size (sheet
->cell_hash
) + 1000;
4162 GPtrArray
*all_cells
;
4163 int last_row
= -1, last_col
= -1;
4164 GnmValue
*res
= NULL
;
4168 if (gnm_debug_flag ("sheet-foreach"))
4169 g_printerr ("Using celllist for area of size %d\n",
4172 range_init (&r
, start_col
, start_row
, end_col
, end_row
);
4173 all_cells
= sheet_cells (sheet
, &r
);
4175 for (ui
= 0; ui
< all_cells
->len
; ui
++) {
4176 GnmCell
*cell
= g_ptr_array_index (all_cells
, ui
);
4179 iter
.pp
.eval
.row
= cell
->pos
.row
;
4180 iter
.pp
.eval
.col
= cell
->pos
.col
;
4182 if (iter
.pp
.eval
.row
!= last_row
) {
4183 last_row
= iter
.pp
.eval
.row
;
4184 iter
.ri
= sheet_row_get (iter
.pp
.sheet
, last_row
);
4186 if (iter
.ri
== NULL
) {
4187 g_critical ("Cell without row data -- please report");
4190 if (visibility_matters
&& !iter
.ri
->visible
)
4192 if (ignore_filtered
&& iter
.ri
->in_filter
&& !iter
.ri
->visible
)
4195 if (iter
.pp
.eval
.col
!= last_col
) {
4196 last_col
= iter
.pp
.eval
.col
;
4197 iter
.ci
= sheet_col_get (iter
.pp
.sheet
, last_col
);
4199 if (iter
.ci
== NULL
) {
4200 g_critical ("Cell without column data -- please report");
4203 if (visibility_matters
&& !iter
.ci
->visible
)
4206 ignore
= (ignore_empty
&&
4207 VALUE_IS_EMPTY (cell
->value
) &&
4208 !gnm_cell_needs_recalc (cell
));
4212 res
= (*callback
) (&iter
, closure
);
4217 g_ptr_array_free (all_cells
, TRUE
);
4221 for (iter
.pp
.eval
.row
= start_row
;
4222 iter
.pp
.eval
.row
<= end_row
;
4223 ++iter
.pp
.eval
.row
) {
4224 iter
.ri
= sheet_row_get (iter
.pp
.sheet
, iter
.pp
.eval
.row
);
4226 /* no need to check visibility, that would require a colinfo to exist */
4227 if (iter
.ri
== NULL
) {
4228 if (only_existing
) {
4229 /* skip segments with no cells */
4230 if (iter
.pp
.eval
.row
== COLROW_SEGMENT_START (iter
.pp
.eval
.row
)) {
4231 ColRowSegment
const *segment
=
4232 COLROW_GET_SEGMENT (&(sheet
->rows
), iter
.pp
.eval
.row
);
4233 if (segment
== NULL
)
4234 iter
.pp
.eval
.row
= COLROW_SEGMENT_END (iter
.pp
.eval
.row
);
4238 for (iter
.pp
.eval
.col
= start_col
; iter
.pp
.eval
.col
<= end_col
; ++iter
.pp
.eval
.col
) {
4239 cont
= (*callback
) (&iter
, closure
);
4248 if (visibility_matters
&& !iter
.ri
->visible
)
4250 if (ignore_filtered
&& iter
.ri
->in_filter
&& !iter
.ri
->visible
)
4253 for (iter
.pp
.eval
.col
= start_col
; iter
.pp
.eval
.col
<= end_col
; ++iter
.pp
.eval
.col
) {
4254 iter
.ci
= sheet_col_get (sheet
, iter
.pp
.eval
.col
);
4255 if (iter
.ci
!= NULL
) {
4256 if (visibility_matters
&& !iter
.ci
->visible
)
4258 iter
.cell
= sheet_cell_get (sheet
,
4259 iter
.pp
.eval
.col
, iter
.pp
.eval
.row
);
4263 ignore
= (iter
.cell
== NULL
)
4264 ? (only_existing
|| ignore_empty
)
4265 : (ignore_empty
&& VALUE_IS_EMPTY (iter
.cell
->value
) &&
4266 !gnm_cell_needs_recalc (iter
.cell
));
4269 if (iter
.pp
.eval
.col
== COLROW_SEGMENT_START (iter
.pp
.eval
.col
)) {
4270 ColRowSegment
const *segment
=
4271 COLROW_GET_SEGMENT (&(sheet
->cols
), iter
.pp
.eval
.col
);
4272 if (segment
== NULL
)
4273 iter
.pp
.eval
.col
= COLROW_SEGMENT_END (iter
.pp
.eval
.col
);
4278 cont
= (*callback
) (&iter
, closure
);
4287 * sheet_cell_foreach:
4289 * @callback: (scope call):
4292 * Call @callback with an argument of @data for each cell in the sheet
4295 sheet_cell_foreach (Sheet
const *sheet
, GHFunc callback
, gpointer data
)
4297 g_return_if_fail (IS_SHEET (sheet
));
4299 g_hash_table_foreach (sheet
->cell_hash
, callback
, data
);
4303 * sheet_cells_count:
4306 * Returns the number of cells with content in the current workbook.
4309 sheet_cells_count (Sheet
const *sheet
)
4311 return g_hash_table_size (sheet
->cell_hash
);
4315 cb_sheet_cells_collect (G_GNUC_UNUSED gpointer unused
,
4316 GnmCell
const *cell
,
4319 GnmEvalPos
*ep
= eval_pos_init_cell (g_new (GnmEvalPos
, 1), cell
);
4320 g_ptr_array_add (cells
, ep
);
4324 * sheet_cell_positions:
4325 * @sheet: The sheet to find cells in.
4326 * @comments: If true, include cells with only comments also.
4328 * Collects a GPtrArray of GnmEvalPos pointers for all cells in a sheet.
4329 * No particular order should be assumed.
4330 * Returns: (element-type GnmEvalPos) (transfer full): the newly created array
4333 sheet_cell_positions (Sheet
*sheet
, gboolean comments
)
4335 GPtrArray
*cells
= g_ptr_array_new ();
4337 g_return_val_if_fail (IS_SHEET (sheet
), cells
);
4339 sheet_cell_foreach (sheet
, (GHFunc
)cb_sheet_cells_collect
, cells
);
4343 GSList
*scomments
, *ptr
;
4345 range_init_full_sheet (&r
, sheet
);
4346 scomments
= sheet_objects_get (sheet
, &r
, GNM_CELL_COMMENT_TYPE
);
4347 for (ptr
= scomments
; ptr
; ptr
= ptr
->next
) {
4348 GnmComment
*c
= ptr
->data
;
4349 GnmRange
const *loc
= sheet_object_get_range (GNM_SO (c
));
4350 GnmCell
*cell
= sheet_cell_get (sheet
, loc
->start
.col
, loc
->start
.row
);
4352 /* If cell does not exist, we haven't seen it... */
4353 GnmEvalPos
*ep
= g_new (GnmEvalPos
, 1);
4355 ep
->eval
.col
= loc
->start
.col
;
4356 ep
->eval
.row
= loc
->start
.row
;
4357 g_ptr_array_add (cells
, ep
);
4360 g_slist_free (scomments
);
4368 cb_fail_if_exist (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
4370 return gnm_cell_is_empty (iter
->cell
) ? NULL
: VALUE_TERMINATE
;
4374 * sheet_is_region_empty:
4375 * @sheet: sheet to check
4376 * @r: region to check
4378 * Returns: %TRUE if the specified region of the @sheet does not
4382 sheet_is_region_empty (Sheet
*sheet
, GnmRange
const *r
)
4384 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
4386 return sheet_foreach_cell_in_range (
4387 sheet
, CELL_ITER_IGNORE_BLANK
, r
,
4388 cb_fail_if_exist
, NULL
) == NULL
;
4392 sheet_is_cell_empty (Sheet
*sheet
, int col
, int row
)
4394 GnmCell
const *cell
= sheet_cell_get (sheet
, col
, row
);
4395 return gnm_cell_is_empty (cell
);
4399 * sheet_cell_add_to_hash:
4400 * @sheet The sheet where the cell is inserted
4401 * @cell The cell, it should already have col/pos pointers
4402 * initialized pointing to the correct ColRowInfo
4404 * GnmCell::pos must be valid before this is called. The position is used as the
4408 sheet_cell_add_to_hash (Sheet
*sheet
, GnmCell
*cell
)
4410 g_return_if_fail (cell
->pos
.col
< gnm_sheet_get_max_cols (sheet
));
4411 g_return_if_fail (cell
->pos
.row
< gnm_sheet_get_max_rows (sheet
));
4413 cell
->base
.flags
|= GNM_CELL_IN_SHEET_LIST
;
4415 * fetching the col/row here serve 3 functions
4416 * 1) obsolete: we used to store the pointer in the cell
4417 * 2) Expanding col/row.max_used
4418 * 3) Creating an entry in the COLROW_SEGMENT. Lots and lots of
4419 * things use those to help limit iteration
4421 * For now just call col_fetch even though it is not necessary to
4422 * ensure that 2,3 still happen. Alot will need rewriting to avoid
4423 * these requirements.
4425 (void)sheet_col_fetch (sheet
, cell
->pos
.col
);
4426 (void)sheet_row_fetch (sheet
, cell
->pos
.row
);
4428 gnm_cell_unrender (cell
);
4430 g_hash_table_insert (sheet
->cell_hash
, cell
, cell
);
4432 if (gnm_sheet_merge_is_corner (sheet
, &cell
->pos
))
4433 cell
->base
.flags
|= GNM_CELL_IS_MERGED
;
4436 #undef USE_CELL_POOL
4438 #ifdef USE_CELL_POOL
4439 /* The pool from which all cells are allocated. */
4440 static GOMemChunk
*cell_pool
;
4442 static int cell_allocations
= 0;
4449 #ifdef USE_CELL_POOL
4450 go_mem_chunk_alloc0 (cell_pool
)
4452 (cell_allocations
++, g_slice_new0 (GnmCell
))
4456 cell
->base
.flags
= DEPENDENT_CELL
;
4462 cell_free (GnmCell
*cell
)
4464 g_return_if_fail (cell
!= NULL
);
4466 gnm_cell_cleanout (cell
);
4467 #ifdef USE_CELL_POOL
4468 go_mem_chunk_free (cell_pool
, cell
);
4470 cell_allocations
--, g_slice_free1 (sizeof (*cell
), cell
);
4475 * gnm_sheet_cell_init: (skip)
4478 gnm_sheet_cell_init (void)
4480 #ifdef USE_CELL_POOL
4481 cell_pool
= go_mem_chunk_new ("cell pool",
4487 #ifdef USE_CELL_POOL
4489 cb_cell_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
4491 GnmCell
const *cell
= data
;
4492 g_printerr ("Leaking cell %p at %s\n", (void *)cell
, cell_name (cell
));
4497 * gnm_sheet_cell_shutdown: (skip)
4500 gnm_sheet_cell_shutdown (void)
4502 #ifdef USE_CELL_POOL
4503 go_mem_chunk_foreach_leak (cell_pool
, cb_cell_pool_leak
, NULL
);
4504 go_mem_chunk_destroy (cell_pool
, FALSE
);
4507 if (cell_allocations
)
4508 g_printerr ("Leaking %d cells.\n", cell_allocations
);
4512 /****************************************************************************/
4515 * sheet_cell_create:
4520 * Creates a new cell and adds it to the sheet hash.
4523 sheet_cell_create (Sheet
*sheet
, int col
, int row
)
4527 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
4528 g_return_val_if_fail (col
>= 0, NULL
);
4529 g_return_val_if_fail (col
< gnm_sheet_get_max_cols (sheet
), NULL
);
4530 g_return_val_if_fail (row
>= 0, NULL
);
4531 g_return_val_if_fail (row
< gnm_sheet_get_max_rows (sheet
), NULL
);
4534 cell
->base
.sheet
= sheet
;
4535 cell
->pos
.col
= col
;
4536 cell
->pos
.row
= row
;
4537 cell
->value
= value_new_empty ();
4539 sheet_cell_add_to_hash (sheet
, cell
);
4544 * sheet_cell_remove_from_hash:
4548 * Removes a cell from the sheet hash, clears any spans, and unlinks it from
4549 * the dependent collection.
4552 sheet_cell_remove_from_hash (Sheet
*sheet
, GnmCell
*cell
)
4554 cell_unregister_span (cell
);
4555 if (gnm_cell_expr_is_linked (cell
))
4556 dependent_unlink (GNM_CELL_TO_DEP (cell
));
4557 g_hash_table_remove (sheet
->cell_hash
, cell
);
4558 cell
->base
.flags
&= ~(GNM_CELL_IN_SHEET_LIST
|GNM_CELL_IS_MERGED
);
4562 * sheet_cell_destroy:
4567 * Remove the cell from the web of dependencies of a
4568 * sheet. Do NOT redraw.
4571 sheet_cell_destroy (Sheet
*sheet
, GnmCell
*cell
, gboolean queue_recalc
)
4573 if (gnm_cell_expr_is_linked (cell
)) {
4574 /* if it needs recalc then its depends are already queued
4575 * check recalc status before we unlink
4577 queue_recalc
&= !gnm_cell_needs_recalc (cell
);
4578 dependent_unlink (GNM_CELL_TO_DEP (cell
));
4582 cell_foreach_dep (cell
, (GnmDepFunc
)dependent_queue_recalc
, NULL
);
4584 sheet_cell_remove_from_hash (sheet
, cell
);
4589 * sheet_cell_remove:
4595 * Remove the cell from the web of dependencies of a
4596 * sheet. Do NOT free the cell, optionally redraw it, optionally
4597 * queue it for recalc.
4600 sheet_cell_remove (Sheet
*sheet
, GnmCell
*cell
,
4601 gboolean redraw
, gboolean queue_recalc
)
4603 g_return_if_fail (cell
!= NULL
);
4604 g_return_if_fail (IS_SHEET (sheet
));
4606 /* Queue a redraw on the region used by the cell being removed */
4608 sheet_redraw_region (sheet
,
4609 cell
->pos
.col
, cell
->pos
.row
,
4610 cell
->pos
.col
, cell
->pos
.row
);
4611 sheet_flag_status_update_cell (cell
);
4614 sheet_cell_destroy (sheet
, cell
, queue_recalc
);
4618 cb_free_cell (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
4620 sheet_cell_destroy (iter
->pp
.sheet
, iter
->cell
, FALSE
);
4625 * sheet_col_destroy:
4630 * Destroys a ColRowInfo from the Sheet with all of its cells
4633 sheet_col_destroy (Sheet
*sheet
, int const col
, gboolean free_cells
)
4635 ColRowSegment
**segment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (&(sheet
->cols
), col
);
4636 int const sub
= COLROW_SUB_INDEX (col
);
4637 ColRowInfo
*ci
= NULL
;
4639 if (*segment
== NULL
)
4641 ci
= (*segment
)->info
[sub
];
4645 if (sheet
->cols
.max_outline_level
> 0 &&
4646 sheet
->cols
.max_outline_level
== ci
->outline_level
)
4647 sheet
->priv
->recompute_max_col_group
= TRUE
;
4650 sheet_foreach_cell_in_region (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4652 &cb_free_cell
, NULL
);
4654 (*segment
)->info
[sub
] = NULL
;
4657 /* Use >= just in case things are screwed up */
4658 if (col
>= sheet
->cols
.max_used
) {
4660 while (--i
>= 0 && sheet_col_get (sheet
, i
) == NULL
)
4662 sheet
->cols
.max_used
= i
;
4667 * Destroys a row ColRowInfo
4670 sheet_row_destroy (Sheet
*sheet
, int const row
, gboolean free_cells
)
4672 ColRowSegment
**segment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
4673 int const sub
= COLROW_SUB_INDEX (row
);
4674 ColRowInfo
*ri
= NULL
;
4676 if (*segment
== NULL
)
4678 ri
= (*segment
)->info
[sub
];
4682 if (sheet
->rows
.max_outline_level
> 0 &&
4683 sheet
->rows
.max_outline_level
== ri
->outline_level
)
4684 sheet
->priv
->recompute_max_row_group
= TRUE
;
4687 sheet_foreach_cell_in_region (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4689 &cb_free_cell
, NULL
);
4691 /* Rows have span lists, destroy them too */
4692 row_destroy_span (ri
);
4694 (*segment
)->info
[sub
] = NULL
;
4697 /* Use >= just in case things are screwed up */
4698 if (row
>= sheet
->rows
.max_used
) {
4700 while (--i
>= 0 && sheet_row_get (sheet
, i
) == NULL
)
4702 sheet
->rows
.max_used
= i
;
4707 cb_remove_allcells (G_GNUC_UNUSED gpointer ignore0
, GnmCell
*cell
, G_GNUC_UNUSED gpointer ignore1
)
4709 cell
->base
.flags
&= ~GNM_CELL_IN_SHEET_LIST
;
4714 sheet_destroy_contents (Sheet
*sheet
)
4719 /* By the time we reach here dependencies should have been shut down */
4720 g_return_if_fail (sheet
->deps
== NULL
);
4722 /* A simple test to see if this has already been run. */
4723 if (sheet
->hash_merged
== NULL
)
4727 GSList
*tmp
= sheet
->slicers
;
4728 sheet
->slicers
= NULL
;
4729 g_slist_free_full (tmp
, (GDestroyNotify
)gnm_sheet_slicer_clear_sheet
);
4732 /* These contain SheetObjects, remove them first */
4733 filters
= g_slist_copy (sheet
->filters
);
4734 g_slist_foreach (filters
, (GFunc
)gnm_filter_remove
, NULL
);
4735 g_slist_foreach (filters
, (GFunc
)gnm_filter_unref
, NULL
);
4736 g_slist_free (filters
);
4738 if (sheet
->sheet_objects
) {
4739 /* The list is changed as we remove */
4740 GSList
*objs
= g_slist_copy (sheet
->sheet_objects
);
4742 for (ptr
= objs
; ptr
!= NULL
; ptr
= ptr
->next
) {
4743 SheetObject
*so
= GNM_SO (ptr
->data
);
4745 sheet_object_clear_sheet (so
);
4747 g_slist_free (objs
);
4748 if (sheet
->sheet_objects
!= NULL
)
4749 g_warning ("There is a problem with sheet objects");
4752 /* The memory is managed by Sheet::list_merged */
4753 g_hash_table_destroy (sheet
->hash_merged
);
4754 sheet
->hash_merged
= NULL
;
4756 g_slist_free_full (sheet
->list_merged
, g_free
);
4757 sheet
->list_merged
= NULL
;
4759 /* Clear the row spans 1st */
4760 for (i
= sheet
->rows
.max_used
; i
>= 0 ; --i
)
4761 row_destroy_span (sheet_row_get (sheet
, i
));
4763 /* Remove all the cells */
4764 sheet_cell_foreach (sheet
, (GHFunc
) &cb_remove_allcells
, NULL
);
4765 g_hash_table_destroy (sheet
->cell_hash
);
4767 /* Delete in ascending order to avoid decrementing max_used each time */
4768 for (i
= 0; i
<= sheet
->cols
.max_used
; ++i
)
4769 sheet_col_destroy (sheet
, i
, FALSE
);
4771 for (i
= 0; i
<= sheet
->rows
.max_used
; ++i
)
4772 sheet_row_destroy (sheet
, i
, FALSE
);
4774 /* Free segments too */
4775 col_row_collection_resize (&sheet
->cols
, 0);
4776 g_ptr_array_free (sheet
->cols
.info
, TRUE
);
4777 sheet
->cols
.info
= NULL
;
4779 col_row_collection_resize (&sheet
->rows
, 0);
4780 g_ptr_array_free (sheet
->rows
.info
, TRUE
);
4781 sheet
->rows
.info
= NULL
;
4783 g_clear_object (&sheet
->solver_parameters
);
4788 * @sheet: the sheet to destroy
4790 * Please note that you need to detach this sheet before
4791 * calling this routine or you will get a warning.
4794 sheet_destroy (Sheet
*sheet
)
4796 g_return_if_fail (IS_SHEET (sheet
));
4798 if (sheet
->sheet_views
->len
> 0)
4799 g_warning ("Unexpected left over views");
4801 if (sheet
->print_info
) {
4802 gnm_print_info_free (sheet
->print_info
);
4803 sheet
->print_info
= NULL
;
4806 style_color_unref (sheet
->tab_color
);
4807 sheet
->tab_color
= NULL
;
4808 style_color_unref (sheet
->tab_text_color
);
4809 sheet
->tab_text_color
= NULL
;
4811 gnm_app_clipboard_invalidate_sheet (sheet
);
4815 gnm_sheet_finalize (GObject
*obj
)
4817 Sheet
*sheet
= SHEET (obj
);
4818 gboolean debug_FMR
= gnm_debug_flag ("sheet-fmr");
4820 sheet_destroy (sheet
);
4822 g_clear_object (&sheet
->solver_parameters
);
4824 gnm_conventions_unref (sheet
->convs
);
4825 sheet
->convs
= NULL
;
4827 g_list_free_full (sheet
->scenarios
, g_object_unref
);
4828 sheet
->scenarios
= NULL
;
4830 if (sheet
->sort_setups
!= NULL
)
4831 g_hash_table_unref (sheet
->sort_setups
);
4833 dependents_invalidate_sheet (sheet
, TRUE
);
4835 sheet_destroy_contents (sheet
);
4837 if (sheet
->slicers
!= NULL
) {
4838 g_warning ("DataSlicer list should be NULL");
4840 if (sheet
->filters
!= NULL
) {
4841 g_warning ("Filter list should be NULL");
4843 if (sheet
->sheet_objects
!= NULL
) {
4844 g_warning ("Sheet object list should be NULL");
4846 if (sheet
->list_merged
!= NULL
) {
4847 g_warning ("Merged list should be NULL");
4849 if (sheet
->hash_merged
!= NULL
) {
4850 g_warning ("Merged hash should be NULL");
4853 sheet_style_shutdown (sheet
);
4855 (void) g_idle_remove_by_data (sheet
);
4858 g_printerr ("Sheet %p is %s\n", sheet
, sheet
->name_quoted
);
4860 g_free (sheet
->name_quoted
);
4861 g_free (sheet
->name_unquoted
);
4862 g_free (sheet
->name_unquoted_collate_key
);
4863 g_free (sheet
->name_case_insensitive
);
4865 sheet
->name_quoted
= (char *)0xdeadbeef;
4866 sheet
->name_unquoted
= (char *)0xdeadbeef;
4867 g_free (sheet
->priv
);
4868 g_ptr_array_free (sheet
->sheet_views
, TRUE
);
4870 gnm_rvc_free (sheet
->rendered_values
);
4873 /* Keep object around. */
4877 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
4880 /*****************************************************************************/
4883 * cb_empty_cell: A callback for sheet_foreach_cell_in_region
4884 * removes/clear all of the cells in the specified region.
4885 * Does NOT queue a redraw.
4887 * WARNING : This does NOT regenerate spans that were interrupted by
4888 * this cell and can now continue.
4891 cb_empty_cell (GnmCellIter
const *iter
, gpointer user
)
4893 int clear_flags
= GPOINTER_TO_INT (user
);
4895 /* TODO : here and other places flag a need to update the
4898 if (row
>= sheet
->rows
.max_used
|| col
>= sheet
->cols
.max_used
) { }
4901 sheet_cell_remove (iter
->pp
.sheet
, iter
->cell
, FALSE
,
4902 (clear_flags
& CLEAR_RECALC_DEPS
) &&
4903 iter
->pp
.wb
->recursive_dirty_enabled
);
4909 * sheet_clear_region:
4915 * @clear_flags: If this is %TRUE then styles are erased.
4918 * Clears are region of cells
4920 * We assemble a list of cells to destroy, since we will be making changes
4921 * to the structure being manipulated by the sheet_foreach_cell_in_region routine
4924 sheet_clear_region (Sheet
*sheet
,
4925 int start_col
, int start_row
,
4926 int end_col
, int end_row
,
4932 g_return_if_fail (IS_SHEET (sheet
));
4933 g_return_if_fail (start_col
<= end_col
);
4934 g_return_if_fail (start_row
<= end_row
);
4936 r
.start
.col
= start_col
;
4937 r
.start
.row
= start_row
;
4938 r
.end
.col
= end_col
;
4939 r
.end
.row
= end_row
;
4941 if (clear_flags
& CLEAR_VALUES
&& !(clear_flags
& CLEAR_NOCHECKARRAY
) &&
4942 sheet_range_splits_array (sheet
, &r
, NULL
, cc
, _("Clear")))
4945 /* Queue a redraw for cells being modified */
4946 if (clear_flags
& (CLEAR_VALUES
|CLEAR_FORMATS
))
4947 sheet_redraw_region (sheet
,
4948 start_col
, start_row
,
4951 /* Clear the style in the region (new_default will ref the style for us). */
4952 if (clear_flags
& CLEAR_FORMATS
) {
4953 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
4954 sheet_range_calc_spans (sheet
, &r
, GNM_SPANCALC_RE_RENDER
|GNM_SPANCALC_RESIZE
);
4955 rows_height_update (sheet
, &r
, TRUE
);
4958 if (clear_flags
& CLEAR_OBJECTS
)
4959 sheet_objects_clear (sheet
, &r
, G_TYPE_NONE
, NULL
);
4960 else if (clear_flags
& CLEAR_COMMENTS
)
4961 sheet_objects_clear (sheet
, &r
, GNM_CELL_COMMENT_TYPE
, NULL
);
4963 /* TODO : how to handle objects ? */
4964 if (clear_flags
& CLEAR_VALUES
) {
4965 /* Remove or empty the cells depending on
4966 * whether or not there are comments
4968 sheet_foreach_cell_in_region (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4969 start_col
, start_row
, end_col
, end_row
,
4970 &cb_empty_cell
, GINT_TO_POINTER (clear_flags
));
4972 if (!(clear_flags
& CLEAR_NORESPAN
)) {
4973 sheet_queue_respan (sheet
, start_row
, end_row
);
4974 sheet_flag_status_update_range (sheet
, &r
);
4978 if (clear_flags
& CLEAR_MERGES
) {
4979 GSList
*merged
, *ptr
;
4980 merged
= gnm_sheet_merge_get_overlap (sheet
, &r
);
4981 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
)
4982 gnm_sheet_merge_remove (sheet
, ptr
->data
);
4983 g_slist_free (merged
);
4986 if (clear_flags
& CLEAR_RECALC_DEPS
)
4987 sheet_region_queue_recalc (sheet
, &r
);
4990 sheet_redraw_all (sheet
, FALSE
);
4994 sheet_clear_region_cb (GnmSheetRange
*sr
, int *flags
)
4996 sheet_clear_region (sr
->sheet
,
4997 sr
->range
.start
.col
, sr
->range
.start
.row
,
4998 sr
->range
.end
.col
, sr
->range
.end
.row
,
4999 *flags
| CLEAR_NOCHECKARRAY
, NULL
);
5004 * sheet_clear_region_undo:
5005 * @sr: #GnmSheetRange
5006 * @clear_flags: flags.
5008 * Returns: (transfer full): the new #GOUndo.
5010 GOUndo
*sheet_clear_region_undo (GnmSheetRange
*sr
, int clear_flags
)
5012 int *flags
= g_new(int, 1);
5013 *flags
= clear_flags
;
5014 return go_undo_binary_new
5015 (sr
, (gpointer
)flags
,
5016 (GOUndoBinaryFunc
) sheet_clear_region_cb
,
5017 (GFreeFunc
) gnm_sheet_range_free
,
5018 (GFreeFunc
) g_free
);
5022 /*****************************************************************************/
5025 sheet_mark_dirty (Sheet
*sheet
)
5027 g_return_if_fail (IS_SHEET (sheet
));
5029 if (sheet
->workbook
)
5030 go_doc_set_dirty (GO_DOC (sheet
->workbook
), TRUE
);
5033 /****************************************************************************/
5036 sheet_cells_deps_move (GnmExprRelocateInfo
*rinfo
)
5038 Sheet
*sheet
= rinfo
->origin_sheet
;
5039 GPtrArray
*deps
= sheet_cells (sheet
, &rinfo
->origin
);
5042 /* Phase 1: collect all cells and remove them from hash. */
5043 for (ui
= 0; ui
< deps
->len
; ui
++) {
5044 GnmCell
*cell
= g_ptr_array_index (deps
, ui
);
5045 gboolean needs_recalc
= gnm_cell_needs_recalc (cell
);
5046 sheet_cell_remove_from_hash (sheet
, cell
);
5047 if (needs_recalc
) /* Do we need this now? */
5048 cell
->base
.flags
|= DEPENDENT_NEEDS_RECALC
;
5051 /* Phase 2: add all non-cell deps with positions */
5052 SHEET_FOREACH_DEPENDENT
5054 GnmCellPos
const *pos
;
5055 if (!dependent_is_cell (dep
) &&
5056 dependent_has_pos (dep
) &&
5057 (pos
= dependent_pos (dep
)) &&
5058 range_contains (&rinfo
->origin
, pos
->col
, pos
->row
)) {
5059 dependent_unlink (dep
);
5060 g_ptr_array_add (deps
, dep
);
5064 /* Phase 3: move everything and add cells to hash. */
5065 for (ui
= 0; ui
< deps
->len
; ui
++) {
5066 GnmDependent
*dep
= g_ptr_array_index (deps
, ui
);
5068 dependent_move (dep
, rinfo
->col_offset
, rinfo
->row_offset
);
5070 if (dependent_is_cell (dep
))
5071 sheet_cell_add_to_hash (sheet
, GNM_DEP_TO_CELL (dep
));
5074 dependent_link (dep
);
5077 g_ptr_array_free (deps
, TRUE
);
5080 /* Moves the headers to their new location */
5082 sheet_colrow_move (Sheet
*sheet
, gboolean is_cols
,
5083 int const old_pos
, int const new_pos
)
5085 ColRowSegment
*segment
= COLROW_GET_SEGMENT (is_cols
? &sheet
->cols
: &sheet
->rows
, old_pos
);
5086 ColRowInfo
*info
= segment
5087 ? segment
->info
[COLROW_SUB_INDEX (old_pos
)]
5090 g_return_if_fail (old_pos
>= 0);
5091 g_return_if_fail (new_pos
>= 0);
5096 /* Update the position */
5097 segment
->info
[COLROW_SUB_INDEX (old_pos
)] = NULL
;
5098 sheet_colrow_add (sheet
, info
, is_cols
, new_pos
);
5102 sheet_colrow_set_collapse (Sheet
*sheet
, gboolean is_cols
, int pos
)
5105 ColRowInfo
const *vs
= NULL
;
5107 if (pos
< 0 || pos
>= colrow_max (is_cols
, sheet
))
5110 /* grab the next or previous col/row */
5111 if ((is_cols
? sheet
->outline_symbols_right
: sheet
->outline_symbols_below
)) {
5113 vs
= sheet_colrow_get (sheet
, pos
-1, is_cols
);
5114 } else if ((pos
+1) < colrow_max (is_cols
, sheet
))
5115 vs
= sheet_colrow_get (sheet
, pos
+1, is_cols
);
5117 /* handle the case where an empty col/row should be marked collapsed */
5118 cri
= sheet_colrow_get (sheet
, pos
, is_cols
);
5120 cri
->is_collapsed
= (vs
!= NULL
&& !vs
->visible
&&
5121 vs
->outline_level
> cri
->outline_level
);
5122 else if (vs
!= NULL
&& !vs
->visible
&& vs
->outline_level
> 0) {
5123 cri
= sheet_colrow_fetch (sheet
, pos
, is_cols
);
5124 cri
->is_collapsed
= TRUE
;
5129 combine_undo (GOUndo
**pundo
, GOUndo
*u
)
5132 *pundo
= go_undo_combine (*pundo
, u
);
5137 typedef gboolean (*ColRowInsDelFunc
) (Sheet
*sheet
, int idx
, int count
,
5138 GOUndo
**pundo
, GOCmdContext
*cc
);
5141 ColRowInsDelFunc func
;
5146 ColRowStateList
*states
;
5151 cb_undo_insdel (ColRowInsDelData
*data
)
5153 data
->func (data
->sheet
, data
->pos
, data
->count
, NULL
, NULL
);
5154 colrow_set_states (data
->sheet
, data
->is_cols
,
5155 data
->state_start
, data
->states
);
5159 cb_undo_insdel_free (ColRowInsDelData
*data
)
5161 colrow_state_list_destroy (data
->states
);
5166 sheet_insdel_colrow (Sheet
*sheet
, int pos
, int count
,
5167 GOUndo
**pundo
, GOCmdContext
*cc
,
5168 gboolean is_cols
, gboolean is_insert
,
5169 const char *description
,
5170 ColRowInsDelFunc opposite
)
5173 GnmRange kill_zone
; /* The range whose contents will be lost. */
5174 GnmRange move_zone
; /* The range whose contents will be moved. */
5175 GnmRange change_zone
; /* The union of kill_zone and move_zone. */
5176 int i
, last_pos
, max_used_pos
;
5177 int kill_start
, kill_end
, move_start
, move_end
;
5178 int scount
= is_insert
? count
: -count
;
5179 ColRowStateList
*states
= NULL
;
5180 GnmExprRelocateInfo reloc_info
;
5182 gboolean sticky_end
= TRUE
;
5184 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
5185 g_return_val_if_fail (count
> 0, TRUE
);
5188 * The main undo for an insert col/row is delete col/row and vice versa.
5189 * In addition to that, we collect undo information that the main undo
5190 * operation will not restore -- for example the contents of the kill
5193 if (pundo
) *pundo
= NULL
;
5195 last_pos
= colrow_max (is_cols
, sheet
) - 1;
5196 max_used_pos
= is_cols
? sheet
->cols
.max_used
: sheet
->rows
.max_used
;
5198 kill_start
= last_pos
- (count
- 1);
5199 kill_end
= last_pos
;
5201 move_end
= kill_start
- 1;
5203 int max_count
= last_pos
+ 1 - pos
;
5204 if (count
> max_count
) {
5209 kill_end
= pos
+ (count
- 1);
5210 move_start
= kill_end
+ 1;
5211 move_end
= last_pos
;
5213 (is_cols
? range_init_cols
: range_init_rows
)
5214 (&kill_zone
, sheet
, kill_start
, kill_end
);
5215 (is_cols
? range_init_cols
: range_init_rows
)
5216 (&move_zone
, sheet
, move_start
, move_end
);
5217 change_zone
= range_union (&kill_zone
, &move_zone
);
5219 /* 0. Check displaced/deleted region and ensure arrays aren't divided. */
5220 if (sheet_range_splits_array (sheet
, &kill_zone
, NULL
, cc
, description
))
5222 if (move_start
<= move_end
&&
5223 sheet_range_splits_array (sheet
, &move_zone
, NULL
, cc
, description
))
5227 * At this point we're committed. Anything that can go wrong should
5228 * have been ruled out already.
5232 g_printerr ("Action = %s at %d count %d\n", description
, pos
, count
);
5233 g_printerr ("Kill zone: %s\n", range_as_string (&kill_zone
));
5236 /* 1. Delete all columns/rows in the kill zone */
5238 combine_undo (pundo
, clipboard_copy_range_undo (sheet
, &kill_zone
));
5239 states
= colrow_get_states (sheet
, is_cols
, kill_start
, kill_end
);
5241 for (i
= MIN (max_used_pos
, kill_end
); i
>= kill_start
; --i
)
5242 (is_cols
? sheet_col_destroy
: sheet_row_destroy
)
5244 /* Brutally discard auto filter objects. Collect the rest for undo. */
5245 sheet_objects_clear (sheet
, &kill_zone
, GNM_FILTER_COMBO_TYPE
, NULL
);
5246 sheet_objects_clear (sheet
, &kill_zone
, G_TYPE_NONE
, pundo
);
5248 reloc_info
.reloc_type
= is_cols
? GNM_EXPR_RELOCATE_COLS
: GNM_EXPR_RELOCATE_ROWS
;
5249 reloc_info
.sticky_end
= sticky_end
;
5250 reloc_info
.origin_sheet
= reloc_info
.target_sheet
= sheet
;
5251 parse_pos_init_sheet (&reloc_info
.pos
, sheet
);
5253 /* 2. Get rid of style dependents, see #741197. */
5254 sheet_style_clear_style_dependents (sheet
, &change_zone
);
5256 /* 3. Invalidate references to kill zone. */
5258 /* Done in the next step. */
5260 reloc_info
.origin
= kill_zone
;
5261 /* Force invalidation: */
5262 reloc_info
.col_offset
= is_cols
? last_pos
+ 1 : 0;
5263 reloc_info
.row_offset
= is_cols
? 0 : last_pos
+ 1;
5264 combine_undo (pundo
, dependents_relocate (&reloc_info
));
5267 /* 4. Fix references to the cells which are moving */
5268 reloc_info
.origin
= is_insert
? change_zone
: move_zone
;
5269 reloc_info
.col_offset
= is_cols
? scount
: 0;
5270 reloc_info
.row_offset
= is_cols
? 0 : scount
;
5271 combine_undo (pundo
, dependents_relocate (&reloc_info
));
5273 /* 5. Move the cells */
5274 sheet_cells_deps_move (&reloc_info
);
5276 /* 6. Move the columns/rows to their new location. */
5278 /* From right to left */
5279 for (i
= max_used_pos
; i
>= pos
; --i
)
5280 sheet_colrow_move (sheet
, is_cols
, i
, i
+ count
);
5282 /* From left to right */
5283 for (i
= pos
+ count
; i
<= max_used_pos
; ++i
)
5284 sheet_colrow_move (sheet
, is_cols
, i
, i
- count
);
5286 sheet_colrow_set_collapse (sheet
, is_cols
, pos
);
5287 sheet_colrow_set_collapse (sheet
, is_cols
,
5288 is_insert
? pos
+ count
: last_pos
- (count
- 1));
5290 /* 7. Move formatting. */
5291 sheet_style_insdel_colrow (&reloc_info
);
5293 /* 8. Move objects. */
5294 sheet_objects_relocate (&reloc_info
, FALSE
, pundo
);
5296 /* 9. Move merges. */
5297 gnm_sheet_merge_relocate (&reloc_info
, pundo
);
5299 /* 10. Move filters. */
5300 gnm_sheet_filter_insdel_colrow (sheet
, is_cols
, is_insert
, pos
, count
, pundo
);
5302 /* Notify sheet of pending updates */
5303 sheet_mark_dirty (sheet
);
5304 sheet
->priv
->recompute_visibility
= TRUE
;
5305 sheet_flag_recompute_spans (sheet
);
5306 sheet_flag_status_update_range (sheet
, &change_zone
);
5308 sheet
->priv
->reposition_objects
.col
= pos
;
5310 sheet
->priv
->reposition_objects
.row
= pos
;
5312 /* WARNING WARNING WARNING
5313 * This is bad practice and should not really be here.
5314 * However, we need to ensure that update is run before
5315 * gnm_sheet_view_panes_insdel_colrow plays with frozen panes, updating those can
5316 * trigger redraws before sheet_update has been called. */
5317 sheet_update (sheet
);
5319 SHEET_FOREACH_VIEW (sheet
, sv
,
5320 gnm_sheet_view_panes_insdel_colrow (sv
, is_cols
, is_insert
, pos
, count
););
5322 /* The main undo is the opposite operation. */
5324 ColRowInsDelData
*data
;
5327 data
= g_new (ColRowInsDelData
, 1);
5328 data
->func
= opposite
;
5329 data
->sheet
= sheet
;
5330 data
->is_cols
= is_cols
;
5332 data
->count
= count
;
5333 data
->states
= states
;
5334 data
->state_start
= kill_start
;
5336 u
= go_undo_unary_new (data
, (GOUndoUnaryFunc
)cb_undo_insdel
,
5337 (GFreeFunc
)cb_undo_insdel_free
);
5339 combine_undo (pundo
, u
);
5342 /* Reapply all filters. */
5343 for (l
= sheet
->filters
; l
; l
= l
->next
) {
5344 GnmFilter
*filter
= l
->data
;
5345 gnm_filter_reapply (filter
);
5352 * sheet_insert_cols:
5354 * @col: At which position we want to insert
5355 * @count: The number of columns to be inserted
5356 * @pundo: (out): (transfer full): (allow-none): undo closure
5360 sheet_insert_cols (Sheet
*sheet
, int col
, int count
,
5361 GOUndo
**pundo
, GOCmdContext
*cc
)
5363 return sheet_insdel_colrow (sheet
, col
, count
, pundo
, cc
,
5365 _("Insert Columns"),
5370 * sheet_delete_cols:
5372 * @col: At which position we want to start deleting columns
5373 * @count: The number of columns to be deleted
5374 * @pundo: (out): (transfer full): (allow-none): undo closure
5375 * @cc: The command context
5378 sheet_delete_cols (Sheet
*sheet
, int col
, int count
,
5379 GOUndo
**pundo
, GOCmdContext
*cc
)
5381 return sheet_insdel_colrow (sheet
, col
, count
, pundo
, cc
,
5383 _("Delete Columns"),
5388 * sheet_insert_rows:
5390 * @row: At which position we want to insert
5391 * @count: The number of rows to be inserted
5392 * @pundo: (out): (transfer full): (allow-none): undo closure
5393 * @cc: The command context
5396 sheet_insert_rows (Sheet
*sheet
, int row
, int count
,
5397 GOUndo
**pundo
, GOCmdContext
*cc
)
5399 return sheet_insdel_colrow (sheet
, row
, count
, pundo
, cc
,
5406 * sheet_delete_rows:
5408 * @row: At which position we want to start deleting rows
5409 * @count: The number of rows to be deleted
5410 * @pundo: (out): (transfer full): (allow-none): undo closure
5411 * @cc: The command context
5414 sheet_delete_rows (Sheet
*sheet
, int row
, int count
,
5415 GOUndo
**pundo
, GOCmdContext
*cc
)
5417 return sheet_insdel_colrow (sheet
, row
, count
, pundo
, cc
,
5424 * Callback for sheet_foreach_cell_in_region to remove a cell from the sheet
5425 * hash, unlink from the dependent collection and put it in a temporary list.
5428 cb_collect_cell (GnmCellIter
const *iter
, gpointer user
)
5431 GnmCell
*cell
= iter
->cell
;
5432 gboolean needs_recalc
= gnm_cell_needs_recalc (cell
);
5434 sheet_cell_remove_from_hash (iter
->pp
.sheet
, cell
);
5435 *l
= g_list_prepend (*l
, cell
);
5437 cell
->base
.flags
|= DEPENDENT_NEEDS_RECALC
;
5445 * @pundo: (out) (optional) (transfer full): undo object
5447 * Move a range as specified in @rinfo report warnings to @cc.
5448 * if @pundo is non-%NULL, invalidate references to the
5449 * target region that are being cleared, and store the undo information
5450 * in @pundo. If it is %NULL do NOT INVALIDATE.
5453 sheet_move_range (GnmExprRelocateInfo
const *rinfo
,
5454 GOUndo
**pundo
, GOCmdContext
*cc
)
5456 GList
*cells
= NULL
;
5459 gboolean out_of_range
;
5461 g_return_if_fail (rinfo
!= NULL
);
5462 g_return_if_fail (IS_SHEET (rinfo
->origin_sheet
));
5463 g_return_if_fail (IS_SHEET (rinfo
->target_sheet
));
5464 g_return_if_fail (rinfo
->origin_sheet
!= rinfo
->target_sheet
||
5465 rinfo
->col_offset
!= 0 ||
5466 rinfo
->row_offset
!= 0);
5468 dst
= rinfo
->origin
;
5469 out_of_range
= range_translate (&dst
, rinfo
->target_sheet
,
5470 rinfo
->col_offset
, rinfo
->row_offset
);
5472 /* Redraw the src region in case anything was spanning */
5473 sheet_redraw_range (rinfo
->origin_sheet
, &rinfo
->origin
);
5475 /* 1. invalidate references to any cells in the destination range that
5476 * are not shared with the src. This must be done before the references
5477 * to from the src range are adjusted because they will point into
5480 if (pundo
!= NULL
) {
5482 if (!out_of_range
) {
5484 GnmExprRelocateInfo reloc_info
;
5486 /* We need to be careful about invalidating references
5487 * to the old content of the destination region. We
5488 * only invalidate references to regions that are
5489 * actually lost. However, this care is only necessary
5490 * if the source and target sheets are the same.
5492 * Handle dst cells being pasted over
5494 if (rinfo
->origin_sheet
== rinfo
->target_sheet
&&
5495 range_overlap (&rinfo
->origin
, &dst
))
5496 invalid
= range_split_ranges (&rinfo
->origin
, &dst
);
5498 invalid
= g_slist_append (NULL
, gnm_range_dup (&dst
));
5500 reloc_info
.origin_sheet
= reloc_info
.target_sheet
= rinfo
->target_sheet
;
5502 /* send to infinity to invalidate, but try to assist
5503 * the relocation heuristics only move in 1
5504 * dimension if possible to give us a chance to be
5505 * smart about partial invalidations */
5506 reloc_info
.col_offset
= gnm_sheet_get_max_cols (rinfo
->target_sheet
);
5507 reloc_info
.row_offset
= gnm_sheet_get_max_rows (rinfo
->target_sheet
);
5508 reloc_info
.sticky_end
= TRUE
;
5509 if (rinfo
->col_offset
== 0) {
5510 reloc_info
.col_offset
= 0;
5511 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_ROWS
;
5512 } else if (rinfo
->row_offset
== 0) {
5513 reloc_info
.row_offset
= 0;
5514 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_COLS
;
5516 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
5518 parse_pos_init_sheet (&reloc_info
.pos
,
5519 rinfo
->origin_sheet
);
5522 GnmRange
*r
= invalid
->data
;
5523 invalid
= g_slist_remove (invalid
, r
);
5524 if (!range_overlap (r
, &rinfo
->origin
)) {
5525 reloc_info
.origin
= *r
;
5526 combine_undo (pundo
,
5527 dependents_relocate (&reloc_info
));
5533 * DO NOT handle src cells moving out the bounds.
5534 * that is handled elsewhere.
5538 /* 2. Fix references to and from the cells which are moving */
5539 combine_undo (pundo
, dependents_relocate (rinfo
));
5542 /* 3. Collect the cells */
5543 sheet_foreach_cell_in_range (rinfo
->origin_sheet
,
5544 CELL_ITER_IGNORE_NONEXISTENT
,
5546 &cb_collect_cell
, &cells
);
5548 /* Reverse list so that we start at the top left (simplifies arrays). */
5549 cells
= g_list_reverse (cells
);
5551 /* 4. Clear the target area and invalidate references to it */
5553 /* we can clear content but not styles from the destination
5554 * region without worrying if it overlaps with the source,
5555 * because we have already extracted the content. However,
5556 * we do need to queue anything that depends on the region for
5558 sheet_clear_region (rinfo
->target_sheet
,
5559 dst
.start
.col
, dst
.start
.row
,
5560 dst
.end
.col
, dst
.end
.row
,
5561 CLEAR_VALUES
|CLEAR_RECALC_DEPS
, cc
);
5563 /* 5. Slide styles BEFORE the cells so that spans get computed properly */
5564 sheet_style_relocate (rinfo
);
5566 /* 6. Insert the cells back */
5567 for (; cells
!= NULL
; cells
= g_list_remove (cells
, cell
)) {
5570 /* check for out of bounds and delete if necessary */
5571 if ((cell
->pos
.col
+ rinfo
->col_offset
) >= gnm_sheet_get_max_cols (rinfo
->target_sheet
) ||
5572 (cell
->pos
.row
+ rinfo
->row_offset
) >= gnm_sheet_get_max_rows (rinfo
->target_sheet
)) {
5577 /* Update the location */
5578 cell
->base
.sheet
= rinfo
->target_sheet
;
5579 cell
->pos
.col
+= rinfo
->col_offset
;
5580 cell
->pos
.row
+= rinfo
->row_offset
;
5581 sheet_cell_add_to_hash (rinfo
->target_sheet
, cell
);
5582 if (gnm_cell_has_expr (cell
))
5583 dependent_link (GNM_CELL_TO_DEP (cell
));
5586 /* 7. Move objects in the range */
5587 sheet_objects_relocate (rinfo
, TRUE
, pundo
);
5588 gnm_sheet_merge_relocate (rinfo
, pundo
);
5590 /* 8. Notify sheet of pending update */
5591 sheet_flag_recompute_spans (rinfo
->origin_sheet
);
5592 sheet_flag_status_update_range (rinfo
->origin_sheet
, &rinfo
->origin
);
5596 sheet_colrow_default_calc (Sheet
*sheet
, double units
,
5597 gboolean is_cols
, gboolean is_pts
)
5599 ColRowInfo
*cri
= is_cols
5600 ? &sheet
->cols
.default_style
5601 : &sheet
->rows
.default_style
;
5603 g_return_if_fail (units
> 0.);
5605 cri
->is_default
= TRUE
;
5606 cri
->hard_size
= FALSE
;
5607 cri
->visible
= TRUE
;
5611 cri
->size_pts
= units
;
5612 colrow_compute_pixels_from_pts (cri
, sheet
, is_cols
, -1);
5614 cri
->size_pixels
= units
;
5615 colrow_compute_pts_from_pixels (cri
, sheet
, is_cols
, -1);
5619 /************************************************************************/
5620 /* Col width support routines.
5624 * sheet_col_get_distance_pts:
5626 * Return the number of points between from_col to to_col
5627 * measured from the upper left corner.
5630 sheet_col_get_distance_pts (Sheet
const *sheet
, int from
, int to
)
5632 ColRowInfo
const *ci
;
5633 double dflt
, pts
= 0., sign
= 1.;
5636 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5645 g_return_val_if_fail (from
>= 0, 1.);
5646 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1.);
5648 /* Do not use sheet_colrow_foreach, it ignores empties */
5649 dflt
= sheet
->cols
.default_style
.size_pts
;
5650 for (i
= from
; i
< to
; ++i
) {
5651 if (NULL
== (ci
= sheet_col_get (sheet
, i
)))
5653 else if (ci
->visible
)
5654 pts
+= ci
->size_pts
;
5657 if (sheet
->display_formulas
)
5664 * sheet_col_get_distance_pixels:
5666 * Return the number of pixels between from_col to to_col
5667 * measured from the upper left corner.
5670 sheet_col_get_distance_pixels (Sheet
const *sheet
, int from
, int to
)
5672 ColRowInfo
const *ci
;
5673 int dflt
, pixels
= 0, sign
= 1;
5676 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5685 g_return_val_if_fail (from
>= 0, 1);
5686 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1);
5688 /* Do not use sheet_colrow_foreach, it ignores empties */
5689 dflt
= sheet_col_get_default_size_pixels (sheet
);
5690 for (i
= from
; i
< to
; ++i
) {
5691 if (NULL
== (ci
= sheet_col_get (sheet
, i
)))
5693 else if (ci
->visible
)
5694 pixels
+= ci
->size_pixels
;
5697 return pixels
* sign
;
5701 * sheet_col_set_size_pts:
5704 * @width_pts: The desired widtht in pts
5705 * @set_by_user: %TRUE if this was done by a user (ie, user manually
5708 * Sets width of a col in pts, INCLUDING left and right margins, and the far
5709 * grid line. This is a low level internal routine. It does NOT redraw,
5710 * or reposition objects.
5713 sheet_col_set_size_pts (Sheet
*sheet
, int col
, double width_pts
,
5714 gboolean set_by_user
)
5718 g_return_if_fail (IS_SHEET (sheet
));
5719 g_return_if_fail (width_pts
> 0.0);
5721 ci
= sheet_col_fetch (sheet
, col
);
5722 ci
->hard_size
= set_by_user
;
5723 if (ci
->size_pts
== width_pts
)
5726 ci
->size_pts
= width_pts
;
5727 colrow_compute_pixels_from_pts (ci
, sheet
, TRUE
, -1);
5729 sheet
->priv
->recompute_visibility
= TRUE
;
5730 sheet_flag_recompute_spans (sheet
);
5731 if (sheet
->priv
->reposition_objects
.col
> col
)
5732 sheet
->priv
->reposition_objects
.col
= col
;
5736 sheet_col_set_size_pixels (Sheet
*sheet
, int col
, int width_pixels
,
5737 gboolean set_by_user
)
5741 g_return_if_fail (IS_SHEET (sheet
));
5742 g_return_if_fail (width_pixels
> 0.0);
5744 ci
= sheet_col_fetch (sheet
, col
);
5745 ci
->hard_size
= set_by_user
;
5746 if (ci
->size_pixels
== width_pixels
)
5749 ci
->size_pixels
= width_pixels
;
5750 colrow_compute_pts_from_pixels (ci
, sheet
, TRUE
, -1);
5752 sheet
->priv
->recompute_visibility
= TRUE
;
5753 sheet_flag_recompute_spans (sheet
);
5754 if (sheet
->priv
->reposition_objects
.col
> col
)
5755 sheet
->priv
->reposition_objects
.col
= col
;
5759 * sheet_col_get_default_size_pts:
5761 * Return the default number of pts in a column, including margins.
5762 * This function returns the raw sum, no rounding etc.
5765 sheet_col_get_default_size_pts (Sheet
const *sheet
)
5767 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5768 return sheet
->cols
.default_style
.size_pts
;
5772 sheet_col_get_default_size_pixels (Sheet
const *sheet
)
5774 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5775 return sheet
->cols
.default_style
.size_pixels
;
5779 sheet_col_set_default_size_pts (Sheet
*sheet
, double width_pts
)
5781 g_return_if_fail (IS_SHEET (sheet
));
5782 g_return_if_fail (width_pts
> 0.);
5784 sheet_colrow_default_calc (sheet
, width_pts
, TRUE
, TRUE
);
5785 sheet
->priv
->recompute_visibility
= TRUE
;
5786 sheet_flag_recompute_spans (sheet
);
5787 sheet
->priv
->reposition_objects
.col
= 0;
5790 sheet_col_set_default_size_pixels (Sheet
*sheet
, int width_pixels
)
5792 g_return_if_fail (IS_SHEET (sheet
));
5794 sheet_colrow_default_calc (sheet
, width_pixels
, TRUE
, FALSE
);
5795 sheet
->priv
->recompute_visibility
= TRUE
;
5796 sheet_flag_recompute_spans (sheet
);
5797 sheet
->priv
->reposition_objects
.col
= 0;
5800 /**************************************************************************/
5801 /* Row height support routines
5805 * sheet_row_get_distance_pts:
5807 * Return the number of points between from_row to to_row
5808 * measured from the upper left corner.
5811 sheet_row_get_distance_pts (Sheet
const *sheet
, int from
, int to
)
5813 ColRowSegment
const *segment
;
5814 ColRowInfo
const *ri
;
5815 double const default_size
= sheet
->rows
.default_style
.size_pts
;
5816 double pts
= 0., sign
= 1.;
5819 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5828 g_return_val_if_fail (from
>= 0, 1.);
5829 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1.);
5831 /* Do not use sheet_colrow_foreach, it ignores empties.
5832 * Optimize this so that long jumps are not quite so horrific
5835 for (i
= from
; i
< to
; ++i
) {
5836 segment
= COLROW_GET_SEGMENT (&(sheet
->rows
), i
);
5838 if (segment
!= NULL
) {
5839 ri
= segment
->info
[COLROW_SUB_INDEX (i
)];
5841 pts
+= default_size
;
5842 else if (ri
->visible
)
5843 pts
+= ri
->size_pts
;
5845 int segment_end
= COLROW_SEGMENT_END (i
)+1;
5846 if (segment_end
> to
)
5848 pts
+= default_size
* (segment_end
- i
);
5857 * sheet_row_get_distance_pixels:
5859 * Return the number of pixels between from_row to to_row
5860 * measured from the upper left corner.
5863 sheet_row_get_distance_pixels (Sheet
const *sheet
, int from
, int to
)
5865 ColRowInfo
const *ci
;
5866 int dflt
, pixels
= 0, sign
= 1;
5869 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5878 g_return_val_if_fail (from
>= 0, 1);
5879 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1);
5881 /* Do not use sheet_colrow_foreach, it ignores empties */
5882 dflt
= sheet_row_get_default_size_pixels (sheet
);
5883 for (i
= from
; i
< to
; ++i
) {
5884 if (NULL
== (ci
= sheet_row_get (sheet
, i
)))
5886 else if (ci
->visible
)
5887 pixels
+= ci
->size_pixels
;
5890 return pixels
* sign
;
5894 * sheet_row_set_size_pts:
5897 * @height_pts: The desired height in pts
5898 * @set_by_user: %TRUE if this was done by a user (ie, user manually
5901 * Sets height of a row in pts, INCLUDING top and bottom margins, and the lower
5902 * grid line. This is a low level internal routine. It does NOT redraw,
5903 * or reposition objects.
5906 sheet_row_set_size_pts (Sheet
*sheet
, int row
, double height_pts
,
5907 gboolean set_by_user
)
5911 g_return_if_fail (IS_SHEET (sheet
));
5912 g_return_if_fail (height_pts
> 0.0);
5914 ri
= sheet_row_fetch (sheet
, row
);
5915 ri
->hard_size
= set_by_user
;
5916 if (ri
->size_pts
== height_pts
)
5919 ri
->size_pts
= height_pts
;
5920 colrow_compute_pixels_from_pts (ri
, sheet
, FALSE
, -1);
5922 sheet
->priv
->recompute_visibility
= TRUE
;
5923 if (sheet
->priv
->reposition_objects
.row
> row
)
5924 sheet
->priv
->reposition_objects
.row
= row
;
5928 * sheet_row_set_size_pixels:
5931 * @height_pixels: The desired height
5932 * @set_by_user: %TRUE if this was done by a user (ie, user manually
5935 * Sets height of a row in pixels, INCLUDING top and bottom margins, and the lower
5939 sheet_row_set_size_pixels (Sheet
*sheet
, int row
, int height_pixels
,
5940 gboolean set_by_user
)
5944 g_return_if_fail (IS_SHEET (sheet
));
5945 g_return_if_fail (height_pixels
> 0);
5947 ri
= sheet_row_fetch (sheet
, row
);
5948 ri
->hard_size
= set_by_user
;
5949 if (ri
->size_pixels
== height_pixels
)
5952 ri
->size_pixels
= height_pixels
;
5953 colrow_compute_pts_from_pixels (ri
, sheet
, FALSE
, -1);
5955 sheet
->priv
->recompute_visibility
= TRUE
;
5956 if (sheet
->priv
->reposition_objects
.row
> row
)
5957 sheet
->priv
->reposition_objects
.row
= row
;
5961 * sheet_row_get_default_size_pts:
5963 * Return the defaul number of units in a row, including margins.
5964 * This function returns the raw sum, no rounding etc.
5967 sheet_row_get_default_size_pts (Sheet
const *sheet
)
5969 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5970 return sheet
->rows
.default_style
.size_pts
;
5974 sheet_row_get_default_size_pixels (Sheet
const *sheet
)
5976 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5977 return sheet
->rows
.default_style
.size_pixels
;
5981 sheet_row_set_default_size_pts (Sheet
*sheet
, double height_pts
)
5983 g_return_if_fail (IS_SHEET (sheet
));
5985 sheet_colrow_default_calc (sheet
, height_pts
, FALSE
, TRUE
);
5986 sheet
->priv
->recompute_visibility
= TRUE
;
5987 sheet
->priv
->reposition_objects
.row
= 0;
5991 sheet_row_set_default_size_pixels (Sheet
*sheet
, int height_pixels
)
5993 g_return_if_fail (IS_SHEET (sheet
));
5995 sheet_colrow_default_calc (sheet
, height_pixels
, FALSE
, FALSE
);
5996 sheet
->priv
->recompute_visibility
= TRUE
;
5997 sheet
->priv
->reposition_objects
.row
= 0;
6000 /****************************************************************************/
6003 sheet_scrollbar_config (Sheet
const *sheet
)
6005 g_return_if_fail (IS_SHEET (sheet
));
6007 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
6008 sc_scrollbar_config (control
););
6011 /*****************************************************************************/
6016 } closure_clone_colrow
;
6019 sheet_clone_colrow_info_item (GnmColRowIter
const *iter
, void *user_data
)
6021 closure_clone_colrow
const *closure
= user_data
;
6022 ColRowInfo
*new_colrow
= sheet_colrow_fetch (closure
->sheet
,
6023 iter
->pos
, closure
->is_column
);
6024 col_row_info_copy (new_colrow
, iter
->cri
);
6029 sheet_dup_colrows (Sheet
const *src
, Sheet
*dst
)
6031 closure_clone_colrow closure
;
6032 int max_col
= MIN (gnm_sheet_get_max_cols (src
), gnm_sheet_get_max_cols (dst
)),
6033 max_row
= MIN (gnm_sheet_get_max_rows (src
), gnm_sheet_get_max_rows (dst
));
6035 closure
.sheet
= dst
;
6036 closure
.is_column
= TRUE
;
6037 sheet_colrow_foreach (src
, TRUE
, 0, max_col
- 1,
6038 &sheet_clone_colrow_info_item
, &closure
);
6039 closure
.is_column
= FALSE
;
6040 sheet_colrow_foreach (src
, FALSE
, 0, max_row
- 1,
6041 &sheet_clone_colrow_info_item
, &closure
);
6043 sheet_col_set_default_size_pixels (dst
,
6044 sheet_col_get_default_size_pixels (src
));
6045 sheet_row_set_default_size_pixels (dst
,
6046 sheet_row_get_default_size_pixels (src
));
6048 dst
->cols
.max_outline_level
= src
->cols
.max_outline_level
;
6049 dst
->rows
.max_outline_level
= src
->rows
.max_outline_level
;
6053 sheet_dup_styles (Sheet
const *src
, Sheet
*dst
)
6055 static GnmCellPos
const corner
= { 0, 0 };
6057 GnmStyleList
*styles
;
6059 sheet_style_set_auto_pattern_color (
6060 dst
, sheet_style_get_auto_pattern_color (src
));
6062 styles
= sheet_style_get_range (src
, range_init_full_sheet (&r
, src
));
6063 sheet_style_set_list (dst
, &corner
, styles
, NULL
, NULL
);
6064 style_list_free (styles
);
6068 sheet_dup_merged_regions (Sheet
const *src
, Sheet
*dst
)
6072 for (ptr
= src
->list_merged
; ptr
!= NULL
; ptr
= ptr
->next
)
6073 gnm_sheet_merge_add (dst
, ptr
->data
, FALSE
, NULL
);
6077 sheet_dup_names (Sheet
const *src
, Sheet
*dst
)
6079 GSList
*names
= gnm_named_expr_collection_list (src
->names
);
6086 parse_pos_init_sheet (&dst_pp
, dst
);
6088 /* Pass 1: add placeholders. */
6089 for (l
= names
; l
; l
= l
->next
) {
6090 GnmNamedExpr
*src_nexpr
= l
->data
;
6091 char const *name
= expr_name_name (src_nexpr
);
6092 GnmNamedExpr
*dst_nexpr
=
6093 gnm_named_expr_collection_lookup (dst
->names
, name
);
6094 GnmExprTop
const *texpr
;
6099 texpr
= gnm_expr_top_new_constant (value_new_empty ());
6100 expr_name_add (&dst_pp
, name
, texpr
, NULL
, TRUE
, NULL
);
6103 /* Pass 2: assign the right expression. */
6104 for (l
= names
; l
; l
= l
->next
) {
6105 GnmNamedExpr
*src_nexpr
= l
->data
;
6106 char const *name
= expr_name_name (src_nexpr
);
6107 GnmNamedExpr
*dst_nexpr
=
6108 gnm_named_expr_collection_lookup (dst
->names
, name
);
6109 GnmExprTop
const *texpr
;
6112 g_warning ("Trouble while duplicating name %s", name
);
6116 if (!dst_nexpr
->is_editable
)
6119 texpr
= gnm_expr_top_relocate_sheet (src_nexpr
->texpr
, src
, dst
);
6120 expr_name_set_expr (dst_nexpr
, texpr
);
6123 g_slist_free (names
);
6127 cb_sheet_cell_copy (G_GNUC_UNUSED gpointer unused
, gpointer key
, gpointer new_sheet_param
)
6129 GnmCell
const *cell
= key
;
6130 Sheet
*dst
= new_sheet_param
;
6132 GnmExprTop
const *texpr
;
6134 g_return_if_fail (dst
!= NULL
);
6135 g_return_if_fail (cell
!= NULL
);
6137 src
= cell
->base
.sheet
;
6138 texpr
= cell
->base
.texpr
;
6140 if (texpr
&& gnm_expr_top_is_array_corner (texpr
)) {
6143 texpr
= gnm_expr_top_relocate_sheet (texpr
, src
, dst
);
6144 gnm_expr_top_get_array_size (texpr
, &cols
, &rows
);
6146 gnm_cell_set_array_formula (dst
,
6147 cell
->pos
.col
, cell
->pos
.row
,
6148 cell
->pos
.col
+ cols
- 1,
6149 cell
->pos
.row
+ rows
- 1,
6150 gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (texpr
))));
6152 gnm_expr_top_unref (texpr
);
6153 } else if (texpr
&& gnm_expr_top_is_array_elem (texpr
, NULL
, NULL
)) {
6154 /* Not a corner -- ignore. */
6156 GnmCell
*new_cell
= sheet_cell_create (dst
, cell
->pos
.col
, cell
->pos
.row
);
6157 if (gnm_cell_has_expr (cell
)) {
6158 texpr
= gnm_expr_top_relocate_sheet (texpr
, src
, dst
);
6159 gnm_cell_set_expr_and_value (new_cell
, texpr
, value_new_empty (), TRUE
);
6160 gnm_expr_top_unref (texpr
);
6162 gnm_cell_set_value (new_cell
, value_dup (cell
->value
));
6167 sheet_dup_cells (Sheet
const *src
, Sheet
*dst
)
6169 sheet_cell_foreach (src
, &cb_sheet_cell_copy
, dst
);
6170 sheet_region_queue_recalc (dst
, NULL
);
6174 sheet_dup_filters (Sheet
const *src
, Sheet
*dst
)
6177 for (ptr
= src
->filters
; ptr
!= NULL
; ptr
= ptr
->next
)
6178 gnm_filter_dup (ptr
->data
, dst
);
6179 dst
->filters
= g_slist_reverse (dst
->filters
);
6184 * @source_sheet: #Sheet
6186 * Create a new Sheet and return it.
6187 * Returns: (transfer full): the newly allocated #Sheet.
6190 sheet_dup (Sheet
const *src
)
6197 g_return_val_if_fail (IS_SHEET (src
), NULL
);
6198 g_return_val_if_fail (src
->workbook
!= NULL
, NULL
);
6201 name
= workbook_sheet_get_free_name (wb
, src
->name_unquoted
,
6203 dst
= sheet_new_with_type (wb
, name
, src
->sheet_type
,
6204 src
->size
.max_cols
, src
->size
.max_rows
);
6207 dst
->protected_allow
= src
->protected_allow
;
6209 "zoom-factor", src
->last_zoom_factor_used
,
6210 "text-is-rtl", src
->text_is_rtl
,
6211 "visibility", src
->visibility
,
6212 "protected", src
->is_protected
,
6213 "display-formulas", src
->display_formulas
,
6214 "display-zeros", !src
->hide_zero
,
6215 "display-grid", !src
->hide_grid
,
6216 "display-column-header", !src
->hide_col_header
,
6217 "display-row-header", !src
->hide_row_header
,
6218 "display-outlines", src
->display_outlines
,
6219 "display-outlines-below", src
->outline_symbols_below
,
6220 "display-outlines-right", src
->outline_symbols_right
,
6221 "conventions", src
->convs
,
6222 "tab-foreground", src
->tab_text_color
,
6223 "tab-background", src
->tab_color
,
6226 gnm_print_info_free (dst
->print_info
);
6227 dst
->print_info
= gnm_print_info_dup (src
->print_info
);
6229 sheet_dup_styles (src
, dst
);
6230 sheet_dup_merged_regions (src
, dst
);
6231 sheet_dup_colrows (src
, dst
);
6232 sheet_dup_names (src
, dst
);
6233 sheet_dup_cells (src
, dst
);
6234 sheet_objects_dup (src
, dst
, NULL
);
6235 sheet_dup_filters (src
, dst
); /* must be after objects */
6237 #warning selection is in view
6238 #warning freeze/thaw is in view
6240 g_object_unref (dst
->solver_parameters
);
6241 dst
->solver_parameters
= gnm_solver_param_dup (src
->solver_parameters
, dst
);
6243 for (l
= src
->scenarios
; l
; l
= l
->next
) {
6244 GnmScenario
*src_sc
= l
->data
;
6245 GnmScenario
*dst_sc
= gnm_scenario_dup (src_sc
, dst
);
6246 dst
->scenarios
= g_list_prepend (dst
->scenarios
, dst_sc
);
6248 dst
->scenarios
= g_list_reverse (dst
->scenarios
);
6250 sheet_mark_dirty (dst
);
6251 sheet_redraw_all (dst
, TRUE
);
6257 * sheet_set_outline_direction:
6259 * @is_cols: %TRUE for columns, %FALSE for rows.
6261 * When changing the placement of outline collapse markers the flags
6262 * need to be recomputed.
6265 sheet_set_outline_direction (Sheet
*sheet
, gboolean is_cols
)
6268 g_return_if_fail (IS_SHEET (sheet
));
6270 /* not particularly efficient, but this is not a hot spot */
6271 for (i
= colrow_max (is_cols
, sheet
); i
-- > 0 ; )
6272 sheet_colrow_set_collapse (sheet
, is_cols
, i
);
6280 * Find the SheetView corresponding to the supplied @wbv.
6281 * Returns: (transfer none): the view.
6284 sheet_get_view (Sheet
const *sheet
, WorkbookView
const *wbv
)
6289 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6291 SHEET_FOREACH_VIEW (sheet
, view
, {
6292 if (sv_wbv (view
) == wbv
)
6299 cb_queue_respan (GnmColRowIter
const *iter
, void *user_data
)
6301 ((ColRowInfo
*)(iter
->cri
))->needs_respan
= TRUE
;
6306 * sheet_queue_respan:
6311 * queues a span generation for the selected rows.
6312 * the caller is responsible for queuing a redraw
6315 sheet_queue_respan (Sheet
const *sheet
, int start_row
, int end_row
)
6317 sheet_colrow_foreach (sheet
, FALSE
, start_row
, end_row
,
6318 cb_queue_respan
, NULL
);
6322 sheet_cell_queue_respan (GnmCell
*cell
)
6324 ColRowInfo
*ri
= sheet_row_get (cell
->base
.sheet
, cell
->pos
.row
);
6325 ri
->needs_respan
= TRUE
;
6330 * sheet_get_comment:
6331 * @sheet: #Sheet const *
6332 * @pos: #GnmCellPos const *
6334 * If there is a cell comment at @pos in @sheet return it.
6336 * Caller does get a reference to the object if it exists.
6337 * Returns: (transfer full): the comment or %NULL.
6340 sheet_get_comment (Sheet
const *sheet
, GnmCellPos
const *pos
)
6348 mr
= gnm_sheet_merge_contains_pos (sheet
, pos
);
6351 comments
= sheet_objects_get (sheet
, mr
, GNM_CELL_COMMENT_TYPE
);
6353 r
.start
= r
.end
= *pos
;
6354 comments
= sheet_objects_get (sheet
, &r
, GNM_CELL_COMMENT_TYPE
);
6359 /* This assumes just one comment per cell. */
6360 res
= comments
->data
;
6361 g_slist_free (comments
);
6366 cb_find_extents (GnmCellIter
const *iter
, GnmCellPos
*extent
)
6368 if (extent
->col
< iter
->pp
.eval
.col
)
6369 extent
->col
= iter
->pp
.eval
.col
;
6370 if (extent
->row
< iter
->pp
.eval
.row
)
6371 extent
->row
= iter
->pp
.eval
.row
;
6377 * @sheet: sheet cells are contained on
6378 * @r: range to trim empty cells from
6379 * @cols: trim from right
6380 * @rows: trim from bottom
6382 * This removes empty rows/cols from the
6383 * right hand or bottom edges of the range
6384 * depending on the value of @cols or @rows.
6386 * Returns: %TRUE if the range was totally empty.
6389 sheet_range_trim (Sheet
const *sheet
, GnmRange
*r
,
6390 gboolean cols
, gboolean rows
)
6392 GnmCellPos extent
= { -1, -1 };
6394 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
6395 g_return_val_if_fail (r
!= NULL
, TRUE
);
6397 sheet_foreach_cell_in_range (
6398 (Sheet
*)sheet
, CELL_ITER_IGNORE_BLANK
, r
,
6399 (CellIterFunc
) cb_find_extents
, &extent
);
6401 if (extent
.col
< 0 || extent
.row
< 0)
6404 r
->end
.col
= extent
.col
;
6406 r
->end
.row
= extent
.row
;
6411 * sheet_range_has_heading:
6412 * @sheet: Sheet to check
6413 * @src: GnmRange to check
6416 * Checks for a header row in @sheet!@src. If top is true it looks for a
6417 * header row from the top and if false it looks for a header col from the
6420 * Returns: %TRUE if @src seems to have a heading
6423 sheet_range_has_heading (Sheet
const *sheet
, GnmRange
const *src
,
6424 gboolean top
, gboolean ignore_styles
)
6426 GnmCell
const *a
, *b
;
6429 /* There is only one row or col */
6431 if (src
->end
.row
<= src
->start
.row
)
6433 length
= src
->end
.col
- src
->start
.col
+ 1;
6435 if (src
->end
.col
<= src
->start
.col
)
6437 length
= src
->end
.row
- src
->start
.row
+ 1;
6440 for (i
= 0; i
< length
; i
++) {
6442 a
= sheet_cell_get (sheet
,
6443 src
->start
.col
+ i
, src
->start
.row
);
6444 b
= sheet_cell_get (sheet
,
6445 src
->start
.col
+ i
, src
->start
.row
+ 1);
6447 a
= sheet_cell_get (sheet
,
6448 src
->start
.col
, src
->start
.row
+ i
);
6449 b
= sheet_cell_get (sheet
,
6450 src
->start
.col
+ 1, src
->start
.row
+ i
);
6454 if (a
== NULL
|| a
->value
== NULL
|| b
== NULL
|| b
->value
== NULL
)
6457 if (VALUE_IS_NUMBER (a
->value
)) {
6458 if (!VALUE_IS_NUMBER (b
->value
))
6460 /* check for style differences */
6461 } else if (a
->value
->v_any
.type
!= b
->value
->v_any
.type
)
6464 /* Look for style differences */
6465 if (!ignore_styles
&&
6466 !gnm_style_equal_header (gnm_cell_get_style (a
),
6467 gnm_cell_get_style (b
), top
))
6475 * gnm_sheet_foreach_name:
6477 * @func: (scope call): #GHFunc
6480 * Executes @func for each name in @sheet.
6483 gnm_sheet_foreach_name (Sheet
const *sheet
, GHFunc func
, gpointer data
)
6485 g_return_if_fail (IS_SHEET (sheet
));
6488 gnm_named_expr_collection_foreach (sheet
->names
, func
, data
);
6492 * gnm_sheet_get_size:
6495 * Returns: (transfer none): the sheet size.
6497 GnmSheetSize
const *
6498 gnm_sheet_get_size (Sheet
const *sheet
)
6500 static const GnmSheetSize default_size
= {
6501 GNM_DEFAULT_COLS
, GNM_DEFAULT_ROWS
6504 if (G_UNLIKELY (!sheet
)) {
6505 g_warning ("NULL sheet in gnm_sheet_get_size!");
6506 /* FIXME: This needs to go. */
6507 return &default_size
;
6510 if (G_UNLIKELY (sheet
->being_constructed
))
6511 g_warning ("Access to sheet size during construction!");
6513 return &sheet
->size
;
6517 * gnm_sheet_get_size2:
6518 * @sheet: #Sheet, might be %NULL
6519 * @wb: #Workbook, must be non %NULL if @sheet is %NULL
6521 * Returns: (transfer none): the sheet size if @sheet is non %NULL, or the
6522 * default sheet size for @wb.
6524 GnmSheetSize
const *
6525 gnm_sheet_get_size2 (Sheet
const *sheet
, Workbook
const *wb
)
6528 ? gnm_sheet_get_size (sheet
)
6529 : workbook_get_sheet_size (wb
);
6533 gnm_sheet_set_solver_params (Sheet
*sheet
, GnmSolverParameters
*param
)
6535 g_return_if_fail (IS_SHEET (sheet
));
6536 g_return_if_fail (GNM_IS_SOLVER_PARAMETERS (param
));
6538 g_object_ref (param
);
6539 g_object_unref (sheet
->solver_parameters
);
6540 sheet
->solver_parameters
= param
;
6543 /* ------------------------------------------------------------------------- */
6546 * gnm_sheet_scenario_new:
6548 * @name: the new scenario name.
6550 * Returns: (transfer full): the newly created #GnmScenario.
6553 gnm_sheet_scenario_new (Sheet
*sheet
, const char *name
)
6558 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6559 g_return_val_if_fail (name
!= NULL
, NULL
);
6561 /* Check if a scenario having the same name already exists. */
6562 if (gnm_sheet_scenario_find (sheet
, name
)) {
6563 GString
*str
= g_string_new (NULL
);
6567 len
= strlen (name
);
6568 if (len
> 1 && name
[len
- 1] == ']') {
6569 for (i
= len
- 2; i
> 0; i
--) {
6570 if (! g_ascii_isdigit (name
[i
]))
6574 tmp
= g_strdup (name
);
6575 if (i
> 0 && name
[i
] == '[')
6578 tmp
= g_strdup (name
);
6580 for (j
= 1; ; j
++) {
6581 g_string_printf (str
, "%s [%d]", tmp
, j
);
6582 if (!gnm_sheet_scenario_find (sheet
, str
->str
)) {
6583 actual_name
= g_string_free (str
, FALSE
);
6589 g_string_free (str
, TRUE
);
6592 actual_name
= g_strdup (name
);
6594 sc
= gnm_scenario_new (actual_name
, sheet
);
6596 g_free (actual_name
);
6602 * gnm_sheet_scenario_find:
6604 * @name: the scenario name.
6606 * Returns: (transfer none): the newly created #GnmScenario.
6609 gnm_sheet_scenario_find (Sheet
*sheet
, const char *name
)
6613 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6614 g_return_val_if_fail (name
!= NULL
, NULL
);
6616 for (l
= sheet
->scenarios
; l
; l
= l
->next
) {
6617 GnmScenario
*sc
= l
->data
;
6618 if (strcmp (name
, sc
->name
) == 0)
6626 * gnm_sheet_scenario_add:
6628 * @sc: (transfer full): #GnmScenario
6632 gnm_sheet_scenario_add (Sheet
*sheet
, GnmScenario
*sc
)
6634 g_return_if_fail (IS_SHEET (sheet
));
6635 g_return_if_fail (GNM_IS_SCENARIO (sc
));
6637 /* We take ownership of the ref. */
6638 sheet
->scenarios
= g_list_append (sheet
->scenarios
, sc
);
6642 gnm_sheet_scenario_remove (Sheet
*sheet
, GnmScenario
*sc
)
6644 g_return_if_fail (IS_SHEET (sheet
));
6645 g_return_if_fail (GNM_IS_SCENARIO (sc
));
6647 sheet
->scenarios
= g_list_remove (sheet
->scenarios
, sc
);
6648 g_object_unref (sc
);
6651 /* ------------------------------------------------------------------------- */
6654 * gnm_sheet_get_sort_setups:
6657 * Returns: (transfer none): the sort setups for @sheet.
6660 gnm_sheet_get_sort_setups (Sheet
*sheet
)
6662 GHashTable
*hash
= sheet
->sort_setups
;
6665 hash
= sheet
->sort_setups
=
6666 g_hash_table_new_full
6667 (g_str_hash
, g_str_equal
,
6668 g_free
, (GDestroyNotify
)gnm_sort_data_destroy
);
6674 gnm_sheet_add_sort_setup (Sheet
*sheet
, char *key
, gpointer setup
)
6676 GHashTable
*hash
= gnm_sheet_get_sort_setups (sheet
);
6678 g_hash_table_insert (hash
, key
, setup
);
6682 * gnm_sheet_find_sort_setup:
6686 * Returns: (transfer none): the found sort setup or %NULL.
6689 gnm_sheet_find_sort_setup (Sheet
*sheet
, char const *key
)
6691 if (sheet
->sort_setups
== NULL
)
6693 return g_hash_table_lookup (sheet
->sort_setups
, key
);
6700 * Returns: (transfer none): the date conventions in effect for the sheet.
6701 * This is purely a convenience function to access the conventions used
6702 * for the workbook. All sheets in a workbook share the same date
6705 GODateConventions
const *
6706 sheet_date_conv (Sheet
const *sheet
)
6708 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6709 return workbook_date_conv (sheet
->workbook
);