1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * sheet.c: Implements the sheet management and per-sheet storage
6 * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
7 * Copyright (C) 1997-1999 Miguel de Icaza (miguel@kernel.org)
8 * Copyright (C) 1999-2009 Morten Welinder (terra@gnome.org)
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of the
13 * License, or (at your option) version 3.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
25 #include <gnumeric-config.h>
29 #include "sheet-view.h"
30 #include "command-context.h"
31 #include "sheet-control.h"
32 #include "sheet-style.h"
33 #include "workbook-priv.h"
34 #include "workbook-control.h"
35 #include "workbook-view.h"
36 #include "parse-util.h"
37 #include "dependent.h"
39 #include "number-match.h"
40 #include "clipboard.h"
41 #include "selection.h"
43 #include "print-info.h"
45 #include "style-color.h"
46 #include "style-font.h"
47 #include "application.h"
51 #include "sheet-merge.h"
52 #include "sheet-private.h"
53 #include "expr-name.h"
55 #include "rendered-value.h"
56 #include "gnumeric-conf.h"
57 #include "sheet-object-impl.h"
58 #include "sheet-object-cell-comment.h"
59 #include <tools/gnm-solver.h>
61 #include "sheet-filter.h"
62 #include "sheet-filter-combo.h"
63 #include "gnm-sheet-slicer.h"
64 #include "scenarios.h"
65 #include "cell-draw.h"
68 #include <goffice/goffice.h>
71 #include <gsf/gsf-impl-utils.h>
76 gnm_sheet_size_copy (GnmSheetSize
*size
)
78 GnmSheetSize
*res
= g_new (GnmSheetSize
, 1);
84 gnm_sheet_size_get_type (void)
89 t
= g_boxed_type_register_static ("GnmSheetSize",
90 (GBoxedCopyFunc
)gnm_sheet_size_copy
,
91 (GBoxedFreeFunc
)g_free
);
97 DETACHED_FROM_WORKBOOK
,
101 static guint signals
[LAST_SIGNAL
] = { 0 };
106 void (*detached_from_workbook
) (Sheet
*, Workbook
*wb
);
108 typedef Sheet GnmSheet
;
117 PROP_DISPLAY_FORMULAS
,
120 PROP_DISPLAY_COLUMN_HEADER
,
121 PROP_DISPLAY_ROW_HEADER
,
122 PROP_DISPLAY_OUTLINES
,
123 PROP_DISPLAY_OUTLINES_BELOW
,
124 PROP_DISPLAY_OUTLINES_RIGHT
,
127 PROP_PROTECTED_ALLOW_EDIT_OBJECTS
,
128 PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
,
129 PROP_PROTECTED_ALLOW_CELL_FORMATTING
,
130 PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
,
131 PROP_PROTECTED_ALLOW_ROW_FORMATTING
,
132 PROP_PROTECTED_ALLOW_INSERT_COLUMNS
,
133 PROP_PROTECTED_ALLOW_INSERT_ROWS
,
134 PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
,
135 PROP_PROTECTED_ALLOW_DELETE_COLUMNS
,
136 PROP_PROTECTED_ALLOW_DELETE_ROWS
,
137 PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
,
138 PROP_PROTECTED_ALLOW_SORT_RANGES
,
139 PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
,
140 PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
,
141 PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
,
154 static void gnm_sheet_finalize (GObject
*obj
);
156 static GObjectClass
*parent_class
;
159 sheet_set_direction (Sheet
*sheet
, gboolean text_is_rtl
)
163 text_is_rtl
= !!text_is_rtl
;
164 if (text_is_rtl
== sheet
->text_is_rtl
)
167 sheet_mark_dirty (sheet
);
169 sheet
->text_is_rtl
= text_is_rtl
;
170 sheet
->priv
->reposition_objects
.col
= 0;
171 sheet_range_calc_spans (sheet
,
172 range_init_full_sheet (&r
, sheet
),
173 GNM_SPANCALC_RE_RENDER
);
177 sheet_set_visibility (Sheet
*sheet
, GnmSheetVisibility visibility
)
179 if (sheet
->visibility
== visibility
)
182 sheet
->visibility
= visibility
;
183 sheet_mark_dirty (sheet
);
187 cb_re_render_formulas (G_GNUC_UNUSED gpointer unused
,
189 G_GNUC_UNUSED gpointer user
)
191 if (gnm_cell_has_expr (cell
)) {
192 gnm_cell_unrender (cell
);
193 sheet_cell_queue_respan (cell
);
198 re_render_formulas (Sheet
const *sheet
)
200 sheet_cell_foreach (sheet
, (GHFunc
)cb_re_render_formulas
, NULL
);
204 sheet_set_conventions (Sheet
*sheet
, GnmConventions
const *convs
)
206 if (sheet
->convs
== convs
)
208 gnm_conventions_unref (sheet
->convs
);
209 sheet
->convs
= gnm_conventions_ref (convs
);
210 if (sheet
->display_formulas
)
211 re_render_formulas (sheet
);
212 SHEET_FOREACH_VIEW (sheet
, sv
,
213 sv
->edit_pos_changed
.content
= TRUE
;);
214 sheet_mark_dirty (sheet
);
217 GnmConventions
const *
218 sheet_get_conventions (Sheet
const *sheet
)
220 g_return_val_if_fail (IS_SHEET (sheet
), gnm_conventions_default
);
226 cb_sheet_set_hide_zeros (G_GNUC_UNUSED gpointer unused
,
228 G_GNUC_UNUSED gpointer user
)
230 if (gnm_cell_is_zero (cell
))
231 gnm_cell_unrender (cell
);
235 sheet_set_hide_zeros (Sheet
*sheet
, gboolean hide
)
238 if (sheet
->hide_zero
== hide
)
241 sheet
->hide_zero
= hide
;
242 sheet_mark_dirty (sheet
);
244 sheet_cell_foreach (sheet
, (GHFunc
)cb_sheet_set_hide_zeros
, NULL
);
248 sheet_set_name (Sheet
*sheet
, char const *new_name
)
250 Workbook
*wb
= sheet
->workbook
;
253 char *new_name_unquoted
;
255 g_return_if_fail (new_name
!= NULL
);
257 /* No change whatsoever. */
258 if (go_str_compare (sheet
->name_unquoted
, new_name
) == 0)
261 /* Mark the sheet dirty unless this is the initial name. */
262 if (sheet
->name_unquoted
)
263 sheet_mark_dirty (sheet
);
265 sucker
= wb
? workbook_sheet_by_name (wb
, new_name
) : NULL
;
266 if (sucker
&& sucker
!= sheet
) {
268 * Prevent a name clash. With this you can swap names by
269 * setting just the two names.
271 char *sucker_name
= workbook_sheet_get_free_name (wb
, new_name
, TRUE
, FALSE
);
273 g_warning ("Renaming %s to %s to avoid clash.\n", sucker
->name_unquoted
, sucker_name
);
275 g_object_set (sucker
, "name", sucker_name
, NULL
);
276 g_free (sucker_name
);
279 attached
= wb
!= NULL
&&
280 sheet
->index_in_wb
!= -1 &&
281 sheet
->name_case_insensitive
;
283 /* FIXME: maybe have workbook_sheet_detach_internal for this. */
285 g_hash_table_remove (wb
->sheet_hash_private
,
286 sheet
->name_case_insensitive
);
288 /* Copy before free. */
289 new_name_unquoted
= g_strdup (new_name
);
291 g_free (sheet
->name_unquoted
);
292 g_free (sheet
->name_quoted
);
293 g_free (sheet
->name_unquoted_collate_key
);
294 g_free (sheet
->name_case_insensitive
);
295 sheet
->name_unquoted
= new_name_unquoted
;
296 sheet
->name_quoted
= g_string_free
297 (gnm_expr_conv_quote (sheet
->convs
, new_name_unquoted
),
299 sheet
->name_unquoted_collate_key
=
300 g_utf8_collate_key (new_name_unquoted
, -1);
301 sheet
->name_case_insensitive
=
302 g_utf8_casefold (new_name_unquoted
, -1);
304 /* FIXME: maybe have workbook_sheet_attach_internal for this. */
306 g_hash_table_insert (wb
->sheet_hash_private
,
307 sheet
->name_case_insensitive
,
310 if (!sheet
->being_constructed
&&
311 sheet
->sheet_type
== GNM_SHEET_DATA
) {
312 /* We have to fix the Sheet_Title name */
316 parse_pos_init_sheet (&pp
, sheet
);
317 nexpr
= expr_name_lookup (&pp
, "Sheet_Title");
319 GnmExprTop
const *texpr
=
320 gnm_expr_top_new_constant
321 (value_new_string (sheet
->name_unquoted
));
322 expr_name_set_expr (nexpr
, texpr
);
327 struct resize_colrow
{
334 cb_colrow_compute_pixels_from_pts (GnmColRowIter
const *iter
,
335 struct resize_colrow
*data
)
337 colrow_compute_pixels_from_pts ((ColRowInfo
*)iter
->cri
,
338 data
->sheet
, data
->is_cols
,
344 cb_clear_rendered_cells (G_GNUC_UNUSED gpointer ignored
, GnmCell
*cell
)
346 if (gnm_cell_get_rendered_value (cell
) != NULL
) {
347 sheet_cell_queue_respan (cell
);
348 gnm_cell_unrender (cell
);
353 sheet_scale_changed (Sheet
*sheet
, gboolean cols_rescaled
, gboolean rows_rescaled
)
355 g_return_if_fail (cols_rescaled
|| rows_rescaled
);
357 /* Then every column and row */
359 struct resize_colrow closure
;
361 closure
.sheet
= sheet
;
362 closure
.is_cols
= TRUE
;
363 closure
.scale
= colrow_compute_pixel_scale (sheet
, TRUE
);
365 colrow_compute_pixels_from_pts (&sheet
->cols
.default_style
,
366 sheet
, TRUE
, closure
.scale
);
367 col_row_collection_foreach (&sheet
->cols
,
368 0, gnm_sheet_get_last_col (sheet
),
369 (ColRowHandler
)&cb_colrow_compute_pixels_from_pts
, &closure
);
372 struct resize_colrow closure
;
374 closure
.sheet
= sheet
;
375 closure
.is_cols
= FALSE
;
376 closure
.scale
= colrow_compute_pixel_scale (sheet
, FALSE
);
378 colrow_compute_pixels_from_pts (&sheet
->rows
.default_style
,
379 sheet
, FALSE
, closure
.scale
);
380 col_row_collection_foreach (&sheet
->rows
, 0, gnm_sheet_get_last_row (sheet
),
381 (ColRowHandler
)&cb_colrow_compute_pixels_from_pts
, &closure
);
384 sheet_cell_foreach (sheet
, (GHFunc
)&cb_clear_rendered_cells
, NULL
);
385 SHEET_FOREACH_CONTROL (sheet
, view
, control
, sc_scale_changed (control
););
389 sheet_set_display_formulas (Sheet
*sheet
, gboolean display
)
392 if (sheet
->display_formulas
== display
)
395 sheet
->display_formulas
= display
;
396 sheet_mark_dirty (sheet
);
397 if (!sheet
->being_constructed
)
398 sheet_scale_changed (sheet
, TRUE
, FALSE
);
402 sheet_set_zoom_factor (Sheet
*sheet
, double factor
)
404 if (fabs (factor
- sheet
->last_zoom_factor_used
) < 1e-6)
406 sheet
->last_zoom_factor_used
= factor
;
407 if (!sheet
->being_constructed
)
408 sheet_scale_changed (sheet
, TRUE
, TRUE
);
412 gnm_sheet_set_property (GObject
*object
, guint property_id
,
413 GValue
const *value
, GParamSpec
*pspec
)
415 Sheet
*sheet
= (Sheet
*)object
;
417 switch (property_id
) {
418 case PROP_SHEET_TYPE
:
419 /* Construction-time only */
420 sheet
->sheet_type
= g_value_get_enum (value
);
423 /* Construction-time only */
424 sheet
->workbook
= g_value_get_object (value
);
427 sheet_set_name (sheet
, g_value_get_string (value
));
430 sheet_set_direction (sheet
, g_value_get_boolean (value
));
432 case PROP_VISIBILITY
:
433 sheet_set_visibility (sheet
, g_value_get_enum (value
));
435 case PROP_DISPLAY_FORMULAS
:
436 sheet_set_display_formulas (sheet
, g_value_get_boolean (value
));
438 case PROP_DISPLAY_ZEROS
:
439 sheet_set_hide_zeros (sheet
, !g_value_get_boolean (value
));
441 case PROP_DISPLAY_GRID
:
442 sheet
->hide_grid
= !g_value_get_boolean (value
);
444 case PROP_DISPLAY_COLUMN_HEADER
:
445 sheet
->hide_col_header
= !g_value_get_boolean (value
);
447 case PROP_DISPLAY_ROW_HEADER
:
448 sheet
->hide_row_header
= !g_value_get_boolean (value
);
450 case PROP_DISPLAY_OUTLINES
:
451 sheet
->display_outlines
= !!g_value_get_boolean (value
);
453 case PROP_DISPLAY_OUTLINES_BELOW
:
454 sheet
->outline_symbols_below
= !!g_value_get_boolean (value
);
456 case PROP_DISPLAY_OUTLINES_RIGHT
:
457 sheet
->outline_symbols_right
= !!g_value_get_boolean (value
);
461 sheet
->is_protected
= !!g_value_get_boolean (value
);
463 case PROP_PROTECTED_ALLOW_EDIT_OBJECTS
:
464 sheet
->protected_allow
.edit_objects
= !!g_value_get_boolean (value
);
466 case PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
:
467 sheet
->protected_allow
.edit_scenarios
= !!g_value_get_boolean (value
);
469 case PROP_PROTECTED_ALLOW_CELL_FORMATTING
:
470 sheet
->protected_allow
.cell_formatting
= !!g_value_get_boolean (value
);
472 case PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
:
473 sheet
->protected_allow
.column_formatting
= !!g_value_get_boolean (value
);
475 case PROP_PROTECTED_ALLOW_ROW_FORMATTING
:
476 sheet
->protected_allow
.row_formatting
= !!g_value_get_boolean (value
);
478 case PROP_PROTECTED_ALLOW_INSERT_COLUMNS
:
479 sheet
->protected_allow
.insert_columns
= !!g_value_get_boolean (value
);
481 case PROP_PROTECTED_ALLOW_INSERT_ROWS
:
482 sheet
->protected_allow
.insert_rows
= !!g_value_get_boolean (value
);
484 case PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
:
485 sheet
->protected_allow
.insert_hyperlinks
= !!g_value_get_boolean (value
);
487 case PROP_PROTECTED_ALLOW_DELETE_COLUMNS
:
488 sheet
->protected_allow
.delete_columns
= !!g_value_get_boolean (value
);
490 case PROP_PROTECTED_ALLOW_DELETE_ROWS
:
491 sheet
->protected_allow
.delete_rows
= !!g_value_get_boolean (value
);
493 case PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
:
494 sheet
->protected_allow
.select_locked_cells
= !!g_value_get_boolean (value
);
496 case PROP_PROTECTED_ALLOW_SORT_RANGES
:
497 sheet
->protected_allow
.sort_ranges
= !!g_value_get_boolean (value
);
499 case PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
:
500 sheet
->protected_allow
.edit_auto_filters
= !!g_value_get_boolean (value
);
502 case PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
:
503 sheet
->protected_allow
.edit_pivottable
= !!g_value_get_boolean (value
);
505 case PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
:
506 sheet
->protected_allow
.select_unlocked_cells
= !!g_value_get_boolean (value
);
509 case PROP_CONVENTIONS
:
510 sheet_set_conventions (sheet
, g_value_get_boxed (value
));
512 case PROP_USE_R1C1
: /* convenience api */
513 sheet_set_conventions (sheet
, !!g_value_get_boolean (value
)
514 ? gnm_conventions_xls_r1c1
: gnm_conventions_default
);
517 case PROP_TAB_FOREGROUND
: {
518 GnmColor
*color
= g_value_dup_boxed (value
);
519 style_color_unref (sheet
->tab_text_color
);
520 sheet
->tab_text_color
= color
;
521 sheet_mark_dirty (sheet
);
524 case PROP_TAB_BACKGROUND
: {
525 GnmColor
*color
= g_value_dup_boxed (value
);
526 style_color_unref (sheet
->tab_color
);
527 sheet
->tab_color
= color
;
528 sheet_mark_dirty (sheet
);
531 case PROP_ZOOM_FACTOR
:
532 sheet_set_zoom_factor (sheet
, g_value_get_double (value
));
535 /* Construction-time only */
536 sheet
->size
.max_cols
= g_value_get_int (value
);
539 /* Construction-time only */
540 sheet
->size
.max_rows
= g_value_get_int (value
);
543 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
549 gnm_sheet_get_property (GObject
*object
, guint property_id
,
550 GValue
*value
, GParamSpec
*pspec
)
552 Sheet
*sheet
= (Sheet
*)object
;
554 switch (property_id
) {
555 case PROP_SHEET_TYPE
:
556 g_value_set_enum (value
, sheet
->sheet_type
);
559 g_value_set_object (value
, sheet
->workbook
);
562 g_value_set_string (value
, sheet
->name_unquoted
);
565 g_value_set_boolean (value
, sheet
->text_is_rtl
);
567 case PROP_VISIBILITY
:
568 g_value_set_enum (value
, sheet
->visibility
);
570 case PROP_DISPLAY_FORMULAS
:
571 g_value_set_boolean (value
, sheet
->display_formulas
);
573 case PROP_DISPLAY_ZEROS
:
574 g_value_set_boolean (value
, !sheet
->hide_zero
);
576 case PROP_DISPLAY_GRID
:
577 g_value_set_boolean (value
, !sheet
->hide_grid
);
579 case PROP_DISPLAY_COLUMN_HEADER
:
580 g_value_set_boolean (value
, !sheet
->hide_col_header
);
582 case PROP_DISPLAY_ROW_HEADER
:
583 g_value_set_boolean (value
, !sheet
->hide_row_header
);
585 case PROP_DISPLAY_OUTLINES
:
586 g_value_set_boolean (value
, sheet
->display_outlines
);
588 case PROP_DISPLAY_OUTLINES_BELOW
:
589 g_value_set_boolean (value
, sheet
->outline_symbols_below
);
591 case PROP_DISPLAY_OUTLINES_RIGHT
:
592 g_value_set_boolean (value
, sheet
->outline_symbols_right
);
596 g_value_set_boolean (value
, sheet
->is_protected
);
598 case PROP_PROTECTED_ALLOW_EDIT_OBJECTS
:
599 g_value_set_boolean (value
, sheet
->protected_allow
.edit_objects
);
601 case PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
:
602 g_value_set_boolean (value
, sheet
->protected_allow
.edit_scenarios
);
604 case PROP_PROTECTED_ALLOW_CELL_FORMATTING
:
605 g_value_set_boolean (value
, sheet
->protected_allow
.cell_formatting
);
607 case PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
:
608 g_value_set_boolean (value
, sheet
->protected_allow
.column_formatting
);
610 case PROP_PROTECTED_ALLOW_ROW_FORMATTING
:
611 g_value_set_boolean (value
, sheet
->protected_allow
.row_formatting
);
613 case PROP_PROTECTED_ALLOW_INSERT_COLUMNS
:
614 g_value_set_boolean (value
, sheet
->protected_allow
.insert_columns
);
616 case PROP_PROTECTED_ALLOW_INSERT_ROWS
:
617 g_value_set_boolean (value
, sheet
->protected_allow
.insert_rows
);
619 case PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
:
620 g_value_set_boolean (value
, sheet
->protected_allow
.insert_hyperlinks
);
622 case PROP_PROTECTED_ALLOW_DELETE_COLUMNS
:
623 g_value_set_boolean (value
, sheet
->protected_allow
.delete_columns
);
625 case PROP_PROTECTED_ALLOW_DELETE_ROWS
:
626 g_value_set_boolean (value
, sheet
->protected_allow
.delete_rows
);
628 case PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
:
629 g_value_set_boolean (value
, sheet
->protected_allow
.select_locked_cells
);
631 case PROP_PROTECTED_ALLOW_SORT_RANGES
:
632 g_value_set_boolean (value
, sheet
->protected_allow
.sort_ranges
);
634 case PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
:
635 g_value_set_boolean (value
, sheet
->protected_allow
.edit_auto_filters
);
637 case PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
:
638 g_value_set_boolean (value
, sheet
->protected_allow
.edit_pivottable
);
640 case PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
:
641 g_value_set_boolean (value
, sheet
->protected_allow
.select_unlocked_cells
);
644 case PROP_CONVENTIONS
:
645 g_value_set_boxed (value
, sheet
->convs
);
647 case PROP_USE_R1C1
: /* convenience api */
648 g_value_set_boolean (value
, sheet
->convs
->r1c1_addresses
);
651 case PROP_TAB_FOREGROUND
:
652 g_value_set_boxed (value
, sheet
->tab_text_color
);
654 case PROP_TAB_BACKGROUND
:
655 g_value_set_boxed (value
, sheet
->tab_color
);
657 case PROP_ZOOM_FACTOR
:
658 g_value_set_double (value
, sheet
->last_zoom_factor_used
);
661 g_value_set_int (value
, sheet
->size
.max_cols
);
664 g_value_set_int (value
, sheet
->size
.max_rows
);
667 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
673 gnm_sheet_constructed (GObject
*obj
)
675 Sheet
*sheet
= SHEET (obj
);
677 if (parent_class
->constructed
)
678 parent_class
->constructed (obj
);
680 /* Now sheet_type, max_cols, and max_rows have been set. */
681 sheet
->being_constructed
= FALSE
;
683 col_row_collection_resize (&sheet
->cols
, sheet
->size
.max_cols
);
684 col_row_collection_resize (&sheet
->rows
, sheet
->size
.max_rows
);
686 sheet
->priv
->reposition_objects
.col
= sheet
->size
.max_cols
;
687 sheet
->priv
->reposition_objects
.row
= sheet
->size
.max_rows
;
689 range_init_full_sheet (&sheet
->priv
->unhidden_region
, sheet
);
690 sheet_style_init (sheet
);
692 sheet
->deps
= gnm_dep_container_new (sheet
);
694 switch (sheet
->sheet_type
) {
696 sheet
->display_formulas
= TRUE
;
698 case GNM_SHEET_OBJECT
:
699 sheet
->hide_grid
= TRUE
;
700 sheet
->hide_col_header
= sheet
->hide_row_header
= TRUE
;
701 colrow_compute_pixels_from_pts (&sheet
->rows
.default_style
,
703 colrow_compute_pixels_from_pts (&sheet
->cols
.default_style
,
706 case GNM_SHEET_DATA
: {
707 /* We have to add permanent names */
708 GnmExprTop
const *texpr
;
710 if (sheet
->name_unquoted
)
711 texpr
= gnm_expr_top_new_constant
712 (value_new_string (sheet
->name_unquoted
));
714 texpr
= gnm_expr_top_new_constant
715 (value_new_error_REF (NULL
));
716 expr_name_perm_add (sheet
, "Sheet_Title",
719 texpr
= gnm_expr_top_new_constant
720 (value_new_error_REF (NULL
));
721 expr_name_perm_add (sheet
, "Print_Area",
726 g_assert_not_reached ();
729 sheet_scale_changed (sheet
, TRUE
, TRUE
);
733 cell_set_hash (GnmCell
const *key
)
735 guint32 r
= key
->pos
.row
;
736 guint32 c
= key
->pos
.col
;
740 h
*= (guint32
)123456789;
742 h
*= (guint32
)123456789;
748 cell_set_equal (GnmCell
const *a
, GnmCell
const *b
)
750 return (a
->pos
.row
== b
->pos
.row
&& a
->pos
.col
== b
->pos
.col
);
754 gnm_sheet_init (Sheet
*sheet
)
756 PangoContext
*context
;
758 sheet
->priv
= g_new0 (SheetPrivate
, 1);
759 sheet
->being_constructed
= TRUE
;
761 sheet
->sheet_views
= g_ptr_array_new ();
763 /* Init, focus, and load handle setting these if/when necessary */
764 sheet
->priv
->recompute_visibility
= TRUE
;
765 sheet
->priv
->recompute_spans
= TRUE
;
767 sheet
->is_protected
= FALSE
;
768 sheet
->protected_allow
.edit_scenarios
= FALSE
;
769 sheet
->protected_allow
.cell_formatting
= FALSE
;
770 sheet
->protected_allow
.column_formatting
= FALSE
;
771 sheet
->protected_allow
.row_formatting
= FALSE
;
772 sheet
->protected_allow
.insert_columns
= FALSE
;
773 sheet
->protected_allow
.insert_rows
= FALSE
;
774 sheet
->protected_allow
.insert_hyperlinks
= FALSE
;
775 sheet
->protected_allow
.delete_columns
= FALSE
;
776 sheet
->protected_allow
.delete_rows
= FALSE
;
777 sheet
->protected_allow
.select_locked_cells
= TRUE
;
778 sheet
->protected_allow
.sort_ranges
= FALSE
;
779 sheet
->protected_allow
.edit_auto_filters
= FALSE
;
780 sheet
->protected_allow
.edit_pivottable
= FALSE
;
781 sheet
->protected_allow
.select_unlocked_cells
= TRUE
;
783 sheet
->hide_zero
= FALSE
;
784 sheet
->display_outlines
= TRUE
;
785 sheet
->outline_symbols_below
= TRUE
;
786 sheet
->outline_symbols_right
= TRUE
;
787 sheet
->tab_color
= NULL
;
788 sheet
->tab_text_color
= NULL
;
789 sheet
->visibility
= GNM_SHEET_VISIBILITY_VISIBLE
;
791 sheet
->text_is_rtl
= (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL
);
793 sheet
->text_is_rtl
= FALSE
;
796 sheet
->sheet_objects
= NULL
;
797 sheet
->max_object_extent
.col
= sheet
->max_object_extent
.row
= 0;
799 sheet
->solver_parameters
= gnm_solver_param_new (sheet
);
801 sheet
->cols
.max_used
= -1;
802 sheet
->cols
.info
= g_ptr_array_new ();
803 sheet_col_set_default_size_pts (sheet
, 48);
805 sheet
->rows
.max_used
= -1;
806 sheet
->rows
.info
= g_ptr_array_new ();
807 sheet_row_set_default_size_pts (sheet
, 12.75);
809 sheet
->print_info
= gnm_print_information_new (FALSE
);
811 sheet
->filters
= NULL
;
812 sheet
->scenarios
= NULL
;
813 sheet
->sort_setups
= NULL
;
814 sheet
->list_merged
= NULL
;
815 sheet
->hash_merged
= g_hash_table_new ((GHashFunc
)&gnm_cellpos_hash
,
816 (GCompareFunc
)&gnm_cellpos_equal
);
818 sheet
->cell_hash
= g_hash_table_new ((GHashFunc
)&cell_set_hash
,
819 (GCompareFunc
)&cell_set_equal
);
821 /* Init preferences */
822 sheet
->convs
= gnm_conventions_ref (gnm_conventions_default
);
824 /* FIXME: probably not here. */
825 /* See also gtk_widget_create_pango_context (). */
826 sheet
->last_zoom_factor_used
= -1; /* Overridden later */
827 context
= gnm_pango_context_get ();
828 sheet
->rendered_values
= gnm_rvc_new (context
, 5000);
829 g_object_unref (context
);
831 /* Init menu states */
832 sheet
->priv
->enable_showhide_detail
= TRUE
;
834 sheet
->names
= gnm_named_expr_collection_new ();
835 sheet
->style_data
= NULL
;
837 sheet
->index_in_wb
= -1;
840 static Sheet the_invalid_sheet
;
841 Sheet
*invalid_sheet
= &the_invalid_sheet
;
844 gnm_sheet_class_init (GObjectClass
*gobject_class
)
846 if (GNM_MAX_COLS
> 364238) {
848 g_warning (_("This is a special version of Gnumeric. It has been compiled\n"
849 "with support for a very large number of columns. Access to the\n"
850 "column named TRUE may conflict with the constant of the same\n"
851 "name. Expect weirdness."));
854 parent_class
= g_type_class_peek_parent (gobject_class
);
856 gobject_class
->set_property
= gnm_sheet_set_property
;
857 gobject_class
->get_property
= gnm_sheet_get_property
;
858 gobject_class
->finalize
= gnm_sheet_finalize
;
859 gobject_class
->constructed
= gnm_sheet_constructed
;
861 g_object_class_install_property (gobject_class
, PROP_SHEET_TYPE
,
862 g_param_spec_enum ("sheet-type",
864 P_("Which type of sheet this is."),
869 G_PARAM_CONSTRUCT_ONLY
));
870 g_object_class_install_property (gobject_class
, PROP_WORKBOOK
,
871 g_param_spec_object ("workbook",
872 P_("Parent workbook"),
873 P_("The workbook in which this sheet lives"),
877 G_PARAM_CONSTRUCT_ONLY
));
878 g_object_class_install_property (gobject_class
, PROP_NAME
,
879 g_param_spec_string ("name",
881 P_("The name of the sheet."),
885 g_object_class_install_property (gobject_class
, PROP_RTL
,
886 g_param_spec_boolean ("text-is-rtl",
888 P_("Text goes from right to left."),
892 g_object_class_install_property (gobject_class
, PROP_VISIBILITY
,
893 g_param_spec_enum ("visibility",
895 P_("How visible the sheet is."),
896 GNM_SHEET_VISIBILITY_TYPE
,
897 GNM_SHEET_VISIBILITY_VISIBLE
,
900 g_object_class_install_property (gobject_class
, PROP_DISPLAY_FORMULAS
,
901 g_param_spec_boolean ("display-formulas",
902 P_("Display Formul\303\246"),
903 P_("Control whether formul\303\246 are shown instead of values."),
907 g_object_class_install_property (gobject_class
, PROP_DISPLAY_ZEROS
,
908 g_param_spec_boolean ("display-zeros", _("Display Zeros"),
909 _("Control whether zeros are shown are blanked out."),
913 g_object_class_install_property (gobject_class
, PROP_DISPLAY_GRID
,
914 g_param_spec_boolean ("display-grid", _("Display Grid"),
915 _("Control whether the grid is shown."),
919 g_object_class_install_property (gobject_class
, PROP_DISPLAY_COLUMN_HEADER
,
920 g_param_spec_boolean ("display-column-header",
921 P_("Display Column Headers"),
922 P_("Control whether column headers are shown."),
926 g_object_class_install_property (gobject_class
, PROP_DISPLAY_ROW_HEADER
,
927 g_param_spec_boolean ("display-row-header",
928 P_("Display Row Headers"),
929 P_("Control whether row headers are shown."),
933 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES
,
934 g_param_spec_boolean ("display-outlines",
935 P_("Display Outlines"),
936 P_("Control whether outlines are shown."),
940 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES_BELOW
,
941 g_param_spec_boolean ("display-outlines-below",
942 P_("Display Outlines Below"),
943 P_("Control whether outline symbols are shown below."),
947 g_object_class_install_property (gobject_class
, PROP_DISPLAY_OUTLINES_RIGHT
,
948 g_param_spec_boolean ("display-outlines-right",
949 P_("Display Outlines Right"),
950 P_("Control whether outline symbols are shown to the right."),
955 g_object_class_install_property (gobject_class
, PROP_PROTECTED
,
956 g_param_spec_boolean ("protected",
958 P_("Sheet is protected."),
959 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
960 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_OBJECTS
,
961 g_param_spec_boolean ("protected-allow-edit-objects",
962 P_("Protected Allow Edit objects"),
963 P_("Allow objects to be edited while a sheet is protected"),
964 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
965 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_SCENARIOS
,
966 g_param_spec_boolean ("protected-allow-edit-scenarios",
967 P_("Protected allow edit scenarios"),
968 P_("Allow scenarios to be edited while a sheet is protected"),
969 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
970 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_CELL_FORMATTING
,
971 g_param_spec_boolean ("protected-allow-cell-formatting",
972 P_("Protected allow cell formatting"),
973 P_("Allow cell format changes while a sheet is protected"),
974 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
975 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_COLUMN_FORMATTING
,
976 g_param_spec_boolean ("protected-allow-column-formatting",
977 P_("Protected allow column formatting"),
978 P_("Allow column formatting while a sheet is protected"),
979 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
980 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_ROW_FORMATTING
,
981 g_param_spec_boolean ("protected-allow-row-formatting",
982 P_("Protected allow row formatting"),
983 P_("Allow row formatting while a sheet is protected"),
984 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
985 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_COLUMNS
,
986 g_param_spec_boolean ("protected-allow-insert-columns",
987 P_("Protected allow insert columns"),
988 P_("Allow columns to be inserted while a sheet is protected"),
989 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
990 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_ROWS
,
991 g_param_spec_boolean ("protected-allow-insert-rows",
992 P_("Protected allow insert rows"),
993 P_("Allow rows to be inserted while a sheet is protected"),
994 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
995 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_INSERT_HYPERLINKS
,
996 g_param_spec_boolean ("protected-allow-insert-hyperlinks",
997 P_("Protected allow insert hyperlinks"),
998 P_("Allow hyperlinks to be inserted while a sheet is protected"),
999 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1000 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_DELETE_COLUMNS
,
1001 g_param_spec_boolean ("protected-allow-delete-columns",
1002 P_("Protected allow delete columns"),
1003 P_("Allow columns to be deleted while a sheet is protected"),
1004 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1005 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_DELETE_ROWS
,
1006 g_param_spec_boolean ("protected-allow-delete-rows",
1007 P_("Protected allow delete rows"),
1008 P_("Allow rows to be deleted while a sheet is protected"),
1009 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1010 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SELECT_LOCKED_CELLS
,
1011 g_param_spec_boolean ("protected-allow-select-locked-cells",
1012 P_("Protected allow select locked cells"),
1013 P_("Allow the user to select locked cells while a sheet is protected"),
1014 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1015 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SORT_RANGES
,
1016 g_param_spec_boolean ("protected-allow-sort-ranges",
1017 P_("Protected allow sort ranges"),
1018 P_("Allow ranges to be sorted while a sheet is protected"),
1019 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1020 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_AUTO_FILTERS
,
1021 g_param_spec_boolean ("protected-allow-edit-auto-filters",
1022 P_("Protected allow edit auto filters"),
1023 P_("Allow auto filters to be edited while a sheet is protected"),
1024 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1025 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_EDIT_PIVOTTABLE
,
1026 g_param_spec_boolean ("protected-allow-edit-pivottable",
1027 P_("Protected allow edit pivottable"),
1028 P_("Allow pivottable to be edited while a sheet is protected"),
1029 FALSE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1030 g_object_class_install_property (gobject_class
, PROP_PROTECTED_ALLOW_SELECT_UNLOCKED_CELLS
,
1031 g_param_spec_boolean ("protected-allow-select-unlocked-cells",
1032 P_("Protected allow select unlocked cells"),
1033 P_("Allow the user to select unlocked cells while a sheet is protected"),
1034 TRUE
, GSF_PARAM_STATIC
| G_PARAM_READWRITE
));
1036 g_object_class_install_property
1037 (gobject_class
, PROP_CONVENTIONS
,
1038 g_param_spec_boxed ("conventions",
1039 P_("Display convention for expressions (default Gnumeric A1)"),
1040 P_("How to format displayed expressions, (A1 vs R1C1, function names, ...)"),
1041 gnm_conventions_get_type (),
1043 G_PARAM_READWRITE
));
1044 g_object_class_install_property (gobject_class
, PROP_USE_R1C1
, /* convenience wrapper to CONVENTIONS */
1045 g_param_spec_boolean ("use-r1c1",
1046 P_("Display convention for expressions as XLS_R1C1 vs default"),
1047 P_("How to format displayed expressions, (a convenience api)"),
1050 G_PARAM_READWRITE
));
1052 g_object_class_install_property (gobject_class
, PROP_TAB_FOREGROUND
,
1053 g_param_spec_boxed ("tab-foreground",
1054 P_("Tab Foreground"),
1055 P_("The foreground color of the tab."),
1058 G_PARAM_READWRITE
));
1059 g_object_class_install_property (gobject_class
, PROP_TAB_BACKGROUND
,
1060 g_param_spec_boxed ("tab-background",
1061 P_("Tab Background"),
1062 P_("The background color of the tab."),
1065 G_PARAM_READWRITE
));
1067 /* What is this doing in sheet? */
1068 g_object_class_install_property (gobject_class
, PROP_ZOOM_FACTOR
,
1069 g_param_spec_double ("zoom-factor",
1071 P_("The level of zoom used for this sheet."),
1076 G_PARAM_READWRITE
));
1078 g_object_class_install_property (gobject_class
, PROP_COLUMNS
,
1079 g_param_spec_int ("columns",
1081 P_("Columns number in the sheet"),
1082 0, GNM_MAX_COLS
, GNM_DEFAULT_COLS
,
1083 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1085 g_object_class_install_property (gobject_class
, PROP_ROWS
,
1086 g_param_spec_int ("rows",
1088 P_("Rows number in the sheet"),
1089 0, GNM_MAX_ROWS
, GNM_DEFAULT_ROWS
,
1090 GSF_PARAM_STATIC
| G_PARAM_READWRITE
| G_PARAM_CONSTRUCT_ONLY
));
1092 signals
[DETACHED_FROM_WORKBOOK
] = g_signal_new
1093 ("detached_from_workbook",
1096 G_STRUCT_OFFSET (GnmSheetClass
, detached_from_workbook
),
1098 g_cclosure_marshal_VOID__OBJECT
,
1099 G_TYPE_NONE
, 1, GNM_WORKBOOK_TYPE
);
1103 GSF_CLASS (GnmSheet
, gnm_sheet
,
1104 gnm_sheet_class_init
, gnm_sheet_init
, G_TYPE_OBJECT
)
1106 /* ------------------------------------------------------------------------- */
1109 gnm_sheet_type_get_type (void)
1111 static GType etype
= 0;
1113 static const GEnumValue values
[] = {
1114 { GNM_SHEET_DATA
, "GNM_SHEET_DATA", "data" },
1115 { GNM_SHEET_OBJECT
, "GNM_SHEET_OBJECT", "object" },
1116 { GNM_SHEET_XLM
, "GNM_SHEET_XLM", "xlm" },
1119 etype
= g_enum_register_static ("GnmSheetType", values
);
1125 gnm_sheet_visibility_get_type (void)
1127 static GType etype
= 0;
1129 static GEnumValue
const values
[] = {
1130 { GNM_SHEET_VISIBILITY_VISIBLE
, "GNM_SHEET_VISIBILITY_VISIBLE", "visible" },
1131 { GNM_SHEET_VISIBILITY_HIDDEN
, "GNM_SHEET_VISIBILITY_HIDDEN", "hidden" },
1132 { GNM_SHEET_VISIBILITY_VERY_HIDDEN
, "GNM_SHEET_VISIBILITY_VERY_HIDDEN", "very-hidden" },
1135 etype
= g_enum_register_static ("GnmSheetVisibility", values
);
1140 /* ------------------------------------------------------------------------- */
1145 return i
> 0 && (i
& (i
- 1)) == 0;
1149 gnm_sheet_valid_size (int cols
, int rows
)
1151 return (cols
>= GNM_MIN_COLS
&&
1152 cols
<= GNM_MAX_COLS
&&
1154 rows
>= GNM_MIN_ROWS
&&
1155 rows
<= GNM_MAX_ROWS
&&
1158 && 0x80000000u
/ (unsigned)(cols
/ 2) >= (unsigned)rows
1164 gnm_sheet_suggest_size (int *cols
, int *rows
)
1166 int c
= GNM_DEFAULT_COLS
;
1167 int r
= GNM_DEFAULT_ROWS
;
1169 while (c
< *cols
&& c
< GNM_MAX_COLS
)
1172 while (r
< *rows
&& r
< GNM_MAX_ROWS
)
1175 while (!gnm_sheet_valid_size (c
, r
)) {
1176 /* Darn! Too large. */
1177 if (*cols
>= GNM_MIN_COLS
&& c
> GNM_MIN_COLS
)
1179 else if (*rows
>= GNM_MIN_ROWS
&& r
> GNM_MIN_ROWS
)
1181 else if (c
> GNM_MIN_COLS
)
1191 static void gnm_sheet_resize_main (Sheet
*sheet
, int cols
, int rows
,
1192 GOCmdContext
*cc
, GOUndo
**pundo
);
1195 cb_sheet_resize (Sheet
*sheet
, const GnmSheetSize
*data
, GOCmdContext
*cc
)
1197 gnm_sheet_resize_main (sheet
, data
->max_cols
, data
->max_rows
,
1202 gnm_sheet_resize_main (Sheet
*sheet
, int cols
, int rows
,
1203 GOCmdContext
*cc
, GOUndo
**pundo
)
1205 int old_cols
, old_rows
;
1206 GnmStyle
**common_col_styles
= NULL
;
1207 GnmStyle
**common_row_styles
= NULL
;
1209 if (pundo
) *pundo
= NULL
;
1211 old_cols
= gnm_sheet_get_max_cols (sheet
);
1212 old_rows
= gnm_sheet_get_max_rows (sheet
);
1213 if (old_cols
== cols
&& old_rows
== rows
)
1216 /* ---------------------------------------- */
1217 /* Gather styles we want to copy into new areas. */
1219 if (cols
> old_cols
) {
1221 common_row_styles
= sheet_style_most_common (sheet
, FALSE
);
1222 for (r
= 0; r
< old_rows
; r
++)
1223 gnm_style_ref (common_row_styles
[r
]);
1225 if (rows
> old_rows
) {
1227 common_col_styles
= sheet_style_most_common (sheet
, TRUE
);
1228 for (c
= 0; c
< old_cols
; c
++)
1229 gnm_style_ref (common_col_styles
[c
]);
1232 /* ---------------------------------------- */
1233 /* Remove the columns and rows that will disappear. */
1235 if (cols
< old_cols
) {
1239 err
= sheet_delete_cols (sheet
, cols
, G_MAXINT
,
1240 pundo
? &u
: NULL
, cc
);
1242 *pundo
= go_undo_combine (*pundo
, u
);
1247 if (rows
< old_rows
) {
1251 err
= sheet_delete_rows (sheet
, rows
, G_MAXINT
,
1252 pundo
? &u
: NULL
, cc
);
1254 *pundo
= go_undo_combine (*pundo
, u
);
1259 /* ---------------------------------------- */
1260 /* Restrict selection. (Not undone.) */
1262 SHEET_FOREACH_VIEW (sheet
, sv
,
1266 GSList
*sel
= selection_get_ranges (sv
, TRUE
);
1267 gboolean any
= FALSE
;
1269 sv_selection_reset (sv
);
1270 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
1271 vis
= new_full
.start
;
1272 for (l
= sel
; l
; l
= l
->next
) {
1273 GnmRange
*r
= l
->data
;
1275 if (range_intersection (&newr
, r
, &new_full
)) {
1276 sv_selection_add_range (sv
, &newr
);
1284 sv_selection_add_pos (sv
, 0, 0,
1285 GNM_SELECTION_MODE_ADD
);
1286 sv_make_cell_visible (sv
, vis
.col
, vis
.row
, FALSE
);
1289 /* ---------------------------------------- */
1290 /* Resize column and row containers. */
1292 col_row_collection_resize (&sheet
->cols
, cols
);
1293 col_row_collection_resize (&sheet
->rows
, rows
);
1295 /* ---------------------------------------- */
1296 /* Resize the dependency containers. */
1299 GSList
*l
, *linked
= NULL
;
1300 /* FIXME: what about dependents in other workbooks? */
1301 WORKBOOK_FOREACH_DEPENDENT
1302 (sheet
->workbook
, dep
,
1304 if (dependent_is_linked (dep
)) {
1305 dependent_unlink (dep
);
1306 linked
= g_slist_prepend (linked
, dep
);
1309 gnm_dep_container_resize (sheet
->deps
, rows
);
1311 for (l
= linked
; l
; l
= l
->next
) {
1312 GnmDependent
*dep
= l
->data
;
1313 dependent_link (dep
);
1316 g_slist_free (linked
);
1318 workbook_queue_all_recalc (sheet
->workbook
);
1321 /* ---------------------------------------- */
1322 /* Resize the styles. */
1324 sheet_style_resize (sheet
, cols
, rows
);
1326 /* ---------------------------------------- */
1327 /* Actually change the properties. */
1329 sheet
->size
.max_cols
= cols
;
1330 sheet
->cols
.max_used
= MIN (sheet
->cols
.max_used
, cols
- 1);
1331 sheet
->size
.max_rows
= rows
;
1332 sheet
->rows
.max_used
= MIN (sheet
->rows
.max_used
, rows
- 1);
1334 if (old_cols
!= cols
)
1335 g_object_notify (G_OBJECT (sheet
), "columns");
1336 if (old_rows
!= rows
)
1337 g_object_notify (G_OBJECT (sheet
), "rows");
1340 GnmSheetSize
*data
= g_new (GnmSheetSize
, 1);
1343 data
->max_cols
= old_cols
;
1344 data
->max_rows
= old_rows
;
1345 u
= go_undo_binary_new (sheet
, data
,
1346 (GOUndoBinaryFunc
)cb_sheet_resize
,
1348 *pundo
= go_undo_combine (*pundo
, u
);
1351 range_init_full_sheet (&sheet
->priv
->unhidden_region
, sheet
);
1353 /* ---------------------------------------- */
1354 /* Apply styles to new areas. */
1356 if (cols
> old_cols
) {
1358 while (r
< old_rows
) {
1360 GnmStyle
*mstyle
= common_row_styles
[r
];
1362 while (r2
+ 1 < old_rows
&&
1363 mstyle
== common_row_styles
[r2
+ 1])
1365 range_init (&rng
, old_cols
, r
, cols
- 1, r2
);
1366 gnm_style_ref (mstyle
);
1367 sheet_apply_style (sheet
, &rng
, mstyle
);
1371 for (r
= 0; r
< old_rows
; r
++)
1372 gnm_style_unref (common_row_styles
[r
]);
1374 g_free (common_row_styles
);
1377 if (rows
> old_rows
) {
1380 while (c
< old_cols
) {
1382 GnmStyle
*mstyle
= common_col_styles
[c
];
1384 while (c2
+ 1 < old_cols
&&
1385 mstyle
== common_col_styles
[c2
+ 1])
1387 range_init (&rng
, c
, old_rows
, c2
, rows
- 1);
1388 gnm_style_ref (mstyle
);
1389 sheet_apply_style (sheet
, &rng
, mstyle
);
1393 if (cols
> old_cols
) {
1395 * Expanded in both directions. One could argue about
1396 * what style to use down here, but we choose the
1397 * last column style.
1399 GnmStyle
*mstyle
= common_col_styles
[old_cols
- 1];
1404 cols
- 1, rows
- 1);
1405 gnm_style_ref (mstyle
);
1406 sheet_apply_style (sheet
, &rng
, mstyle
);
1409 for (c
= 0; c
< old_cols
; c
++)
1410 gnm_style_unref (common_col_styles
[c
]);
1411 g_free (common_col_styles
);
1414 /* ---------------------------------------- */
1416 sheet_redraw_all (sheet
, TRUE
);
1421 go_undo_undo_with_data (*pundo
, cc
);
1422 g_object_unref (*pundo
);
1430 * @cols: the new columns number.
1431 * @rows: the new rows number.
1432 * @cc: #GOCmdContext.
1433 * @perr: will be %TRUE on error.
1435 * Returns: (transfer full): the newly allocated #GOUndo.
1438 gnm_sheet_resize (Sheet
*sheet
, int cols
, int rows
,
1439 GOCmdContext
*cc
, gboolean
*perr
)
1441 GOUndo
*undo
= NULL
;
1443 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1444 g_return_val_if_fail (gnm_sheet_valid_size (cols
, rows
), NULL
);
1446 if (cols
< sheet
->size
.max_cols
|| rows
< sheet
->size
.max_rows
) {
1447 GSList
*overlap
, *l
;
1448 gboolean bad
= FALSE
;
1451 r
.start
.col
= r
.start
.row
= 0;
1452 r
.end
.col
= MIN (cols
, sheet
->size
.max_cols
) - 1;
1453 r
.end
.row
= MIN (rows
, sheet
->size
.max_rows
) - 1;
1455 overlap
= gnm_sheet_merge_get_overlap (sheet
, &r
);
1456 for (l
= overlap
; l
&& !bad
; l
= l
->next
) {
1457 GnmRange
const *m
= l
->data
;
1458 if (!range_contained (m
, &r
)) {
1460 gnm_cmd_context_error_splits_merge (cc
, m
);
1463 g_slist_free (overlap
);
1470 gnm_sheet_resize_main (sheet
, cols
, rows
, cc
, &undo
);
1478 * sheet_new_with_type:
1480 * @name: An unquoted name
1481 * @type: @GnmSheetType
1482 * @columns: The number of columns for the sheet
1483 * @rows: The number of rows for the sheet
1485 * Create a new Sheet of type @type, and associate it with @wb.
1486 * The type cannot be changed later.
1487 * Returns: (transfer full): the newly allocated sheet.
1490 sheet_new_with_type (Workbook
*wb
, char const *name
, GnmSheetType type
,
1491 int columns
, int rows
)
1495 g_return_val_if_fail (wb
!= NULL
, NULL
);
1496 g_return_val_if_fail (name
!= NULL
, NULL
);
1497 g_return_val_if_fail (gnm_sheet_valid_size (columns
, rows
), NULL
);
1499 sheet
= g_object_new (GNM_SHEET_TYPE
,
1505 "zoom-factor", gnm_conf_get_core_gui_window_zoom (),
1508 if (type
== GNM_SHEET_OBJECT
)
1509 print_info_set_paper_orientation (sheet
->print_info
, GTK_PAGE_ORIENTATION_LANDSCAPE
);
1517 * @name: The name for the sheet (unquoted).
1518 * @columns: The requested columns number.
1519 * @rows: The requested rows number.
1521 * Create a new Sheet of type SHEET_DATA, and associate it with @wb.
1522 * The type can not be changed later
1523 * Returns: (transfer full): the newly allocated sheet.
1526 sheet_new (Workbook
*wb
, char const *name
, int columns
, int rows
)
1528 return sheet_new_with_type (wb
, name
, GNM_SHEET_DATA
, columns
, rows
);
1531 /****************************************************************************/
1534 sheet_redraw_all (Sheet
const *sheet
, gboolean headers
)
1536 /* We potentially do a lot of recalcs as part of this, so make sure
1537 stuff that caches sub-computations see the whole thing instead
1538 of clearing between cells. */
1539 gnm_app_recalc_start ();
1540 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1541 sc_redraw_all (control
, headers
););
1542 gnm_app_recalc_finish ();
1546 cb_clear_rendered_values (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
1548 gnm_cell_unrender (iter
->cell
);
1553 * sheet_range_calc_spans:
1554 * @sheet: The sheet,
1555 * @r: the region to update.
1558 * This is used to re-calculate cell dimensions and re-render
1559 * a cell's text. eg. if a format has changed we need to re-render
1560 * the cached version of the rendered text in the cell.
1563 sheet_range_calc_spans (Sheet
*sheet
, GnmRange
const *r
, GnmSpanCalcFlags flags
)
1565 if (flags
& GNM_SPANCALC_RE_RENDER
)
1566 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
1567 r
->start
.col
, r
->start
.row
, r
->end
.col
, r
->end
.row
,
1568 cb_clear_rendered_values
, NULL
);
1569 sheet_queue_respan (sheet
, r
->start
.row
, r
->end
.row
);
1571 /* Redraw the new region in case the span changes */
1572 sheet_redraw_range (sheet
, r
);
1576 sheet_redraw_partial_row (Sheet
const *sheet
, int const row
,
1577 int const start_col
, int const end_col
)
1580 range_init (&r
, start_col
, row
, end_col
, row
);
1581 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1582 sc_redraw_range (control
, &r
););
1586 sheet_redraw_cell (GnmCell
const *cell
)
1588 CellSpanInfo
const * span
;
1589 int start_col
, end_col
, row
;
1590 GnmRange
const *merged
;
1594 g_return_if_fail (cell
!= NULL
);
1596 sheet
= cell
->base
.sheet
;
1597 merged
= gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
1598 if (merged
!= NULL
) {
1599 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
1600 sc_redraw_range (control
, merged
););
1604 row
= cell
->pos
.row
;
1605 start_col
= end_col
= cell
->pos
.col
;
1606 ri
= sheet_row_get (sheet
, row
);
1607 span
= row_span_get (ri
, start_col
);
1610 start_col
= span
->left
;
1611 end_col
= span
->right
;
1614 sheet_redraw_partial_row (sheet
, row
, start_col
, end_col
);
1618 sheet_cell_calc_span (GnmCell
*cell
, GnmSpanCalcFlags flags
)
1620 CellSpanInfo
const * span
;
1622 int min_col
, max_col
, row
;
1623 gboolean render
= (flags
& GNM_SPANCALC_RE_RENDER
) != 0;
1624 gboolean
const resize
= (flags
& GNM_SPANCALC_RESIZE
) != 0;
1625 gboolean existing
= FALSE
;
1626 GnmRange
const *merged
;
1630 g_return_if_fail (cell
!= NULL
);
1632 sheet
= cell
->base
.sheet
;
1633 row
= cell
->pos
.row
;
1635 /* Render & Size any unrendered cells */
1636 if ((flags
& GNM_SPANCALC_RENDER
) && gnm_cell_get_rendered_value (cell
) == NULL
)
1640 if (!gnm_cell_has_expr (cell
))
1641 gnm_cell_render_value ((GnmCell
*)cell
, TRUE
);
1643 gnm_cell_unrender (cell
);
1644 } else if (resize
) {
1645 /* FIXME: what was wanted here? */
1646 /* rendered_value_calc_size (cell); */
1649 /* Is there an existing span ? clear it BEFORE calculating new one */
1650 ri
= sheet_row_get (sheet
, row
);
1651 span
= row_span_get (ri
, cell
->pos
.col
);
1653 GnmCell
const * const other
= span
->cell
;
1655 min_col
= span
->left
;
1656 max_col
= span
->right
;
1658 /* A different cell used to span into this cell, respan that */
1659 if (cell
!= other
) {
1660 int other_left
, other_right
;
1662 cell_unregister_span (other
);
1663 cell_calc_span (other
, &other_left
, &other_right
);
1664 if (min_col
> other_left
)
1665 min_col
= other_left
;
1666 if (max_col
< other_right
)
1667 max_col
= other_right
;
1669 if (other_left
!= other_right
)
1670 cell_register_span (other
, other_left
, other_right
);
1674 min_col
= max_col
= cell
->pos
.col
;
1676 merged
= gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
1677 if (NULL
!= merged
) {
1679 if (min_col
> merged
->start
.col
)
1680 min_col
= merged
->start
.col
;
1681 if (max_col
< merged
->end
.col
)
1682 max_col
= merged
->end
.col
;
1684 sheet_redraw_cell (cell
);
1688 /* Calculate the span of the cell */
1689 cell_calc_span (cell
, &left
, &right
);
1692 if (max_col
< right
)
1695 /* This cell already had an existing span */
1697 /* If it changed, remove the old one */
1698 if (left
!= span
->left
|| right
!= span
->right
)
1699 cell_unregister_span (cell
);
1701 /* unchanged, short curcuit adding the span again */
1706 cell_register_span (cell
, left
, right
);
1709 sheet_redraw_partial_row (sheet
, row
, min_col
, max_col
);
1713 * sheet_apply_style: (skip)
1714 * @sheet: the sheet in which can be found
1715 * @range: the range to which should be applied
1716 * @style: (transfer full): A #GnmStyle partial style
1718 * A mid level routine that applies the supplied partial style @style to the
1719 * target @range and performs the necessary respanning and redrawing.
1722 sheet_apply_style (Sheet
*sheet
,
1723 GnmRange
const *range
,
1726 GnmSpanCalcFlags spanflags
= gnm_style_required_spanflags (style
);
1727 sheet_style_apply_range (sheet
, range
, style
);
1728 /* This also redraws the range: */
1729 sheet_range_calc_spans (sheet
, range
, spanflags
);
1733 * sheet_apply_style_gi: (rename-to sheet_apply_style)
1734 * @sheet: the sheet in which can be found
1735 * @range: the range to which should be applied
1736 * @style: A #GnmStyle partial style
1738 * A mid level routine that applies the supplied partial style @style to the
1739 * target @range and performs the necessary respanning and redrawing.
1742 sheet_apply_style_gi (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*style
)
1744 GnmSpanCalcFlags spanflags
= gnm_style_required_spanflags (style
);
1745 gnm_style_ref (style
);
1746 sheet_style_apply_range (sheet
, range
, style
);
1747 /* This also redraws the range: */
1748 sheet_range_calc_spans (sheet
, range
, spanflags
);
1752 sheet_apply_style_cb (GnmSheetRange
*sr
,
1755 gnm_style_ref (style
);
1756 sheet_apply_style (sr
->sheet
, &sr
->range
, style
);
1757 sheet_flag_style_update_range (sr
->sheet
, &sr
->range
);
1761 * sheet_apply_style_undo:
1762 * @sr: #GnmSheetRange
1765 * Returns: (transfer full): the new #GOUndo.
1768 sheet_apply_style_undo (GnmSheetRange
*sr
,
1771 gnm_style_ref (style
);
1772 return go_undo_binary_new
1773 (sr
, (gpointer
)style
,
1774 (GOUndoBinaryFunc
) sheet_apply_style_cb
,
1775 (GFreeFunc
) gnm_sheet_range_free
,
1776 (GFreeFunc
) gnm_style_unref
);
1782 sheet_apply_border (Sheet
*sheet
,
1783 GnmRange
const *range
,
1784 GnmBorder
**borders
)
1786 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_RE_RENDER
| GNM_SPANCALC_RESIZE
;
1787 sheet_style_apply_border (sheet
, range
, borders
);
1788 /* This also redraws the range: */
1789 sheet_range_calc_spans (sheet
, range
, spanflags
);
1792 /****************************************************************************/
1795 sheet_row_new (Sheet
*sheet
)
1799 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1801 ri
= col_row_info_new ();
1802 *ri
= sheet
->rows
.default_style
;
1803 ri
->is_default
= FALSE
;
1804 ri
->needs_respan
= TRUE
;
1810 sheet_col_new (Sheet
*sheet
)
1814 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1816 ci
= col_row_info_new ();
1817 *ci
= sheet
->cols
.default_style
;
1818 ci
->is_default
= FALSE
;
1824 sheet_colrow_add (Sheet
*sheet
, ColRowInfo
*cp
, gboolean is_cols
, int n
)
1826 ColRowCollection
*info
= is_cols
? &sheet
->cols
: &sheet
->rows
;
1827 ColRowSegment
**psegment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (info
, n
);
1829 g_return_if_fail (n
>= 0);
1830 g_return_if_fail (n
< colrow_max (is_cols
, sheet
));
1832 if (*psegment
== NULL
)
1833 *psegment
= g_new0 (ColRowSegment
, 1);
1834 colrow_free ((*psegment
)->info
[COLROW_SUB_INDEX (n
)]);
1835 (*psegment
)->info
[COLROW_SUB_INDEX (n
)] = cp
;
1837 if (cp
->outline_level
> info
->max_outline_level
)
1838 info
->max_outline_level
= cp
->outline_level
;
1839 if (n
> info
->max_used
) {
1841 sheet
->priv
->resize_scrollbar
= TRUE
;
1846 sheet_reposition_objects (Sheet
const *sheet
, GnmCellPos
const *pos
)
1849 for (ptr
= sheet
->sheet_objects
; ptr
!= NULL
; ptr
= ptr
->next
)
1850 sheet_object_update_bounds (GNM_SO (ptr
->data
), pos
);
1854 * sheet_flag_status_update_cell:
1855 * @cell: The cell that has changed.
1857 * flag the sheet as requiring an update to the status display
1858 * if the supplied cell location is the edit cursor, or part of the
1861 * Will cause the format toolbar, the edit area, and the auto expressions to be
1862 * updated if appropriate.
1865 sheet_flag_status_update_cell (GnmCell
const *cell
)
1867 SHEET_FOREACH_VIEW (cell
->base
.sheet
, sv
,
1868 sv_flag_status_update_pos (sv
, &cell
->pos
););
1872 * sheet_flag_status_update_range:
1874 * @range: If NULL then force an update.
1876 * flag the sheet as requiring an update to the status display
1877 * if the supplied cell location contains the edit cursor, or intersects of
1878 * the selected region.
1880 * Will cause the format toolbar, the edit area, and the auto expressions to be
1881 * updated if appropriate.
1884 sheet_flag_status_update_range (Sheet
const *sheet
, GnmRange
const *range
)
1886 SHEET_FOREACH_VIEW (sheet
, sv
,
1887 sv_flag_status_update_range (sv
, range
););
1891 * sheet_flag_style_update_range:
1892 * @sheet: The sheet being changed
1893 * @range: the range that is changing.
1895 * Flag format changes that will require updating the format indicators.
1898 sheet_flag_style_update_range (Sheet
const *sheet
, GnmRange
const *range
)
1900 SHEET_FOREACH_VIEW (sheet
, sv
,
1901 sv_flag_style_update_range (sv
, range
););
1905 * sheet_flag_recompute_spans:
1908 * Flag the sheet as requiring a full span recomputation the next time
1909 * sheet_update is called.
1912 sheet_flag_recompute_spans (Sheet
const *sheet
)
1914 sheet
->priv
->recompute_spans
= TRUE
;
1918 cb_outline_level (GnmColRowIter
const *iter
, int *outline_level
)
1920 if (*outline_level
< iter
->cri
->outline_level
)
1921 *outline_level
= iter
->cri
->outline_level
;
1926 * sheet_colrow_fit_gutter:
1927 * @sheet: Sheet to change for.
1928 * @is_cols: Column gutter or row gutter?
1930 * Find the current max outline level.
1933 sheet_colrow_fit_gutter (Sheet
const *sheet
, gboolean is_cols
)
1935 int outline_level
= 0;
1936 col_row_collection_foreach (is_cols
? &sheet
->cols
: &sheet
->rows
,
1937 0, colrow_max (is_cols
, sheet
) - 1,
1938 (ColRowHandler
)cb_outline_level
, &outline_level
);
1939 return outline_level
;
1943 * sheet_update_only_grid:
1946 * Should be called after a logical command has finished processing
1947 * to request redraws for any pending events
1950 sheet_update_only_grid (Sheet
const *sheet
)
1954 g_return_if_fail (IS_SHEET (sheet
));
1958 /* be careful these can toggle flags */
1959 if (p
->recompute_max_col_group
) {
1960 sheet_colrow_gutter ((Sheet
*)sheet
, TRUE
,
1961 sheet_colrow_fit_gutter (sheet
, TRUE
));
1962 sheet
->priv
->recompute_max_col_group
= FALSE
;
1964 if (p
->recompute_max_row_group
) {
1965 sheet_colrow_gutter ((Sheet
*)sheet
, FALSE
,
1966 sheet_colrow_fit_gutter (sheet
, FALSE
));
1967 sheet
->priv
->recompute_max_row_group
= FALSE
;
1970 SHEET_FOREACH_VIEW (sheet
, sv
, {
1971 if (sv
->reposition_selection
) {
1972 sv
->reposition_selection
= FALSE
;
1974 /* when moving we cleared the selection before
1977 if (sv
->selections
!= NULL
)
1978 sv_selection_set (sv
, &sv
->edit_pos_real
,
1979 sv
->cursor
.base_corner
.col
,
1980 sv
->cursor
.base_corner
.row
,
1981 sv
->cursor
.move_corner
.col
,
1982 sv
->cursor
.move_corner
.row
);
1986 if (p
->recompute_spans
) {
1987 p
->recompute_spans
= FALSE
;
1988 /* FIXME : I would prefer to use GNM_SPANCALC_RENDER rather than
1989 * RE_RENDER. It only renders those cells which are not
1990 * rendered. The trouble is that when a col changes size we
1991 * need to rerender, but currently nothing marks that.
1993 * hmm, that suggests an approach. maybe I can install a per
1994 * col flag. Then add a flag clearing loop after the
1998 sheet_calc_spans (sheet
, GNM_SPANCALC_RESIZE
|GNM_SPANCALC_RE_RENDER
|
1999 (p
->recompute_visibility
?
2000 SPANCALC_NO_DRAW
: GNM_SPANCALC_SIMPLE
));
2002 sheet_queue_respan (sheet
, 0, gnm_sheet_get_last_row (sheet
));
2005 if (p
->reposition_objects
.row
< gnm_sheet_get_max_rows (sheet
) ||
2006 p
->reposition_objects
.col
< gnm_sheet_get_max_cols (sheet
)) {
2007 SHEET_FOREACH_VIEW (sheet
, sv
, {
2008 if (!p
->resize
&& sv_is_frozen (sv
)) {
2009 if (p
->reposition_objects
.col
< sv
->unfrozen_top_left
.col
||
2010 p
->reposition_objects
.row
< sv
->unfrozen_top_left
.row
) {
2011 SHEET_VIEW_FOREACH_CONTROL(sv
, control
,
2012 sc_resize (control
, FALSE
););
2016 sheet_reposition_objects (sheet
, &p
->reposition_objects
);
2017 p
->reposition_objects
.row
= gnm_sheet_get_max_rows (sheet
);
2018 p
->reposition_objects
.col
= gnm_sheet_get_max_cols (sheet
);
2023 SHEET_FOREACH_CONTROL (sheet
, sv
, control
, sc_resize (control
, FALSE
););
2026 if (p
->recompute_visibility
) {
2027 /* TODO : There is room for some opimization
2028 * We only need to force complete visibility recalculation
2029 * (which we do in sheet_compute_visible_region)
2030 * if a row or col before the start of the visible region.
2031 * If we are REALLY smart we could even accumulate the size differential
2034 p
->recompute_visibility
= FALSE
;
2035 p
->resize_scrollbar
= FALSE
; /* compute_visible_region does this */
2036 SHEET_FOREACH_CONTROL(sheet
, view
, control
,
2037 sc_recompute_visible_region (control
, TRUE
););
2038 sheet_redraw_all (sheet
, TRUE
);
2041 if (p
->resize_scrollbar
) {
2042 sheet_scrollbar_config (sheet
);
2043 p
->resize_scrollbar
= FALSE
;
2045 if (p
->filters_changed
) {
2046 p
->filters_changed
= FALSE
;
2047 SHEET_FOREACH_CONTROL (sheet
, sv
, sc
,
2048 wb_control_menu_state_update (sc_wbc (sc
), MS_ADD_VS_REMOVE_FILTER
););
2056 * Should be called after a logical command has finished processing to request
2057 * redraws for any pending events, and to update the various status regions
2060 sheet_update (Sheet
const *sheet
)
2062 g_return_if_fail (IS_SHEET (sheet
));
2064 sheet_update_only_grid (sheet
);
2066 SHEET_FOREACH_VIEW (sheet
, sv
, sv_update (sv
););
2071 * @sheet: The sheet where we want to locate the cell
2072 * @col: the cell column
2073 * @row: the cell row
2075 * Return value: (nullable): a #GnmCell, or %NULL if the cell does not exist
2078 sheet_cell_get (Sheet
const *sheet
, int col
, int row
)
2083 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2087 cell
= g_hash_table_lookup (sheet
->cell_hash
, &key
);
2094 * @sheet: The sheet where we want to locate the cell
2095 * @col: the cell column
2096 * @row: the cell row
2098 * Return value: a #GnmCell containing at (@col,@row).
2099 * If no cell existed at that location before, it is created.
2102 sheet_cell_fetch (Sheet
*sheet
, int col
, int row
)
2106 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2108 cell
= sheet_cell_get (sheet
, col
, row
);
2110 cell
= sheet_cell_create (sheet
, col
, row
);
2116 * sheet_colrow_can_group:
2121 * Returns TRUE if the cols/rows in @r.start -> @r.end can be grouped, return
2122 * FALSE otherwise. You can invert the result if you need to find out if a
2123 * group can be ungrouped.
2126 sheet_colrow_can_group (Sheet
*sheet
, GnmRange
const *r
, gboolean is_cols
)
2128 ColRowInfo
const *start_cri
, *end_cri
;
2131 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
2134 start
= r
->start
.col
;
2137 start
= r
->start
.row
;
2140 start_cri
= sheet_colrow_fetch (sheet
, start
, is_cols
);
2141 end_cri
= sheet_colrow_fetch (sheet
, end
, is_cols
);
2143 /* Groups on outline level 0 (no outline) may always be formed */
2144 if (start_cri
->outline_level
== 0 || end_cri
->outline_level
== 0)
2147 /* We just won't group a group that already exists (or doesn't), it's useless */
2148 return (colrow_find_outline_bound (sheet
, is_cols
, start
, start_cri
->outline_level
, FALSE
) != start
||
2149 colrow_find_outline_bound (sheet
, is_cols
, end
, end_cri
->outline_level
, TRUE
) != end
);
2153 sheet_colrow_group_ungroup (Sheet
*sheet
, GnmRange
const *r
,
2154 gboolean is_cols
, gboolean group
)
2156 int i
, new_max
, start
, end
;
2157 int const step
= group
? 1 : -1;
2159 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
2161 /* Can we group/ungroup ? */
2162 if (group
!= sheet_colrow_can_group (sheet
, r
, is_cols
))
2166 start
= r
->start
.col
;
2169 start
= r
->start
.row
;
2173 /* Set new outline for each col/row and find highest outline level */
2174 new_max
= (is_cols
? &sheet
->cols
: &sheet
->rows
)->max_outline_level
;
2175 for (i
= start
; i
<= end
; i
++) {
2176 ColRowInfo
*cri
= sheet_colrow_fetch (sheet
, i
, is_cols
);
2177 int const new_level
= cri
->outline_level
+ step
;
2179 if (new_level
>= 0) {
2180 col_row_info_set_outline (cri
, new_level
, FALSE
);
2181 if (new_max
< new_level
)
2182 new_max
= new_level
;
2187 new_max
= sheet_colrow_fit_gutter (sheet
, is_cols
);
2189 sheet_colrow_gutter (sheet
, is_cols
, new_max
);
2190 SHEET_FOREACH_VIEW (sheet
, sv
,
2191 sv_redraw_headers (sv
, is_cols
, !is_cols
, NULL
););
2197 * sheet_colrow_gutter:
2202 * Set the maximum outline levels for cols or rows.
2205 sheet_colrow_gutter (Sheet
*sheet
, gboolean is_cols
, int max_outline
)
2207 ColRowCollection
*infos
;
2209 g_return_if_fail (IS_SHEET (sheet
));
2211 infos
= is_cols
? &(sheet
->cols
) : &(sheet
->rows
);
2212 if (infos
->max_outline_level
!= max_outline
) {
2213 sheet
->priv
->resize
= TRUE
;
2214 infos
->max_outline_level
= max_outline
;
2218 struct sheet_extent_data
{
2220 gboolean spans_and_merges_extend
;
2221 gboolean ignore_empties
;
2222 gboolean include_hidden
;
2226 cb_sheet_get_extent (G_GNUC_UNUSED gpointer ignored
, gpointer value
, gpointer data
)
2228 GnmCell
const *cell
= (GnmCell
const *) value
;
2229 struct sheet_extent_data
*res
= data
;
2230 Sheet
*sheet
= cell
->base
.sheet
;
2231 ColRowInfo
*ri
= NULL
;
2233 if (res
->ignore_empties
&& gnm_cell_is_empty (cell
))
2235 if (!res
->include_hidden
) {
2236 ri
= sheet_col_get (sheet
, cell
->pos
.col
);
2239 ri
= sheet_row_get (sheet
, cell
->pos
.row
);
2244 /* Remember the first cell is the min & max */
2245 if (res
->range
.start
.col
> cell
->pos
.col
)
2246 res
->range
.start
.col
= cell
->pos
.col
;
2247 if (res
->range
.end
.col
< cell
->pos
.col
)
2248 res
->range
.end
.col
= cell
->pos
.col
;
2249 if (res
->range
.start
.row
> cell
->pos
.row
)
2250 res
->range
.start
.row
= cell
->pos
.row
;
2251 if (res
->range
.end
.row
< cell
->pos
.row
)
2252 res
->range
.end
.row
= cell
->pos
.row
;
2254 if (!res
->spans_and_merges_extend
)
2257 /* Cannot span AND merge */
2258 if (gnm_cell_is_merged (cell
)) {
2259 GnmRange
const *merged
=
2260 gnm_sheet_merge_is_corner (sheet
, &cell
->pos
);
2261 res
->range
= range_union (&res
->range
, merged
);
2263 CellSpanInfo
const *span
;
2265 ri
= sheet_row_get (sheet
, cell
->pos
.row
);
2266 if (ri
->needs_respan
)
2267 row_calc_spans (ri
, cell
->pos
.row
, sheet
);
2268 span
= row_span_get (ri
, cell
->pos
.col
);
2270 if (res
->range
.start
.col
> span
->left
)
2271 res
->range
.start
.col
= span
->left
;
2272 if (res
->range
.end
.col
< span
->right
)
2273 res
->range
.end
.col
= span
->right
;
2281 * @spans_and_merges_extend: optionally extend region for spans and merges.
2282 * @include_hidden: whether to include the content of hidden cells.
2284 * calculates the area occupied by cell data.
2286 * NOTE: When spans_and_merges_extend is TRUE, this function will calculate
2287 * all spans. That might be expensive.
2289 * NOTE: This refers to *visible* contents. Cells with empty values, including
2290 * formulas with such values, are *ignored.
2292 * Return value: the range.
2295 sheet_get_extent (Sheet
const *sheet
, gboolean spans_and_merges_extend
, gboolean include_hidden
)
2297 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2298 struct sheet_extent_data closure
;
2301 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2303 closure
.range
.start
.col
= gnm_sheet_get_last_col (sheet
) + 1;
2304 closure
.range
.start
.row
= gnm_sheet_get_last_row (sheet
) + 1;
2305 closure
.range
.end
.col
= 0;
2306 closure
.range
.end
.row
= 0;
2307 closure
.spans_and_merges_extend
= spans_and_merges_extend
;
2308 closure
.include_hidden
= include_hidden
;
2309 closure
.ignore_empties
= TRUE
;
2311 sheet_cell_foreach (sheet
, &cb_sheet_get_extent
, &closure
);
2313 for (ptr
= sheet
->sheet_objects
; ptr
; ptr
= ptr
->next
) {
2314 SheetObject
*so
= GNM_SO (ptr
->data
);
2316 closure
.range
.start
.col
= MIN (so
->anchor
.cell_bound
.start
.col
,
2317 closure
.range
.start
.col
);
2318 closure
.range
.start
.row
= MIN (so
->anchor
.cell_bound
.start
.row
,
2319 closure
.range
.start
.row
);
2320 closure
.range
.end
.col
= MAX (so
->anchor
.cell_bound
.end
.col
,
2321 closure
.range
.end
.col
);
2322 closure
.range
.end
.row
= MAX (so
->anchor
.cell_bound
.end
.row
,
2323 closure
.range
.end
.row
);
2326 if (closure
.range
.start
.col
> gnm_sheet_get_last_col (sheet
))
2327 closure
.range
.start
.col
= 0;
2328 if (closure
.range
.start
.row
> gnm_sheet_get_last_row (sheet
))
2329 closure
.range
.start
.row
= 0;
2330 if (closure
.range
.end
.col
< 0)
2331 closure
.range
.end
.col
= 0;
2332 if (closure
.range
.end
.row
< 0)
2333 closure
.range
.end
.row
= 0;
2335 return closure
.range
;
2339 * sheet_get_cells_extent:
2342 * calculates the area occupied by cells, including empty cells.
2344 * Return value: the range.
2347 sheet_get_cells_extent (Sheet
const *sheet
)
2349 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2350 struct sheet_extent_data closure
;
2352 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2354 closure
.range
.start
.col
= gnm_sheet_get_last_col (sheet
);
2355 closure
.range
.start
.row
= gnm_sheet_get_last_row (sheet
);
2356 closure
.range
.end
.col
= 0;
2357 closure
.range
.end
.row
= 0;
2358 closure
.spans_and_merges_extend
= FALSE
;
2359 closure
.include_hidden
= TRUE
;
2360 closure
.ignore_empties
= FALSE
;
2362 sheet_cell_foreach (sheet
, &cb_sheet_get_extent
, &closure
);
2364 return closure
.range
;
2369 sheet_get_nominal_printarea (Sheet
const *sheet
)
2371 GnmNamedExpr
*nexpr
;
2375 GnmRangeRef
const *r_ref
;
2379 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2381 parse_pos_init_sheet (&pos
, sheet
);
2382 nexpr
= expr_name_lookup (&pos
, "Print_Area");
2386 val
= gnm_expr_top_get_range (nexpr
->texpr
);
2387 r_ref
= val
? value_get_rangeref (val
) : NULL
;
2388 if (r_ref
== NULL
) {
2389 value_release (val
);
2393 r
= g_new0 (GnmRange
, 1);
2394 range_init_rangeref (r
, r_ref
);
2395 value_release (val
);
2397 if (r
->end
.col
>= (max_cols
= gnm_sheet_get_max_cols (sheet
)))
2398 r
->end
.col
= max_cols
- 1;
2399 if (r
->end
.row
>= (max_rows
= gnm_sheet_get_max_rows (sheet
)))
2400 r
->end
.row
= max_rows
- 1;
2401 if (r
->start
.col
< 0)
2403 if (r
->start
.row
< 0)
2410 sheet_get_printarea (Sheet
const *sheet
,
2411 gboolean include_styles
,
2412 gboolean ignore_printarea
)
2414 static GnmRange
const dummy
= { { 0,0 }, { 0,0 } };
2415 GnmRange print_area
;
2417 g_return_val_if_fail (IS_SHEET (sheet
), dummy
);
2419 if (!ignore_printarea
) {
2420 GnmRange
*r
= sheet_get_nominal_printarea (sheet
);
2428 print_area
= sheet_get_extent (sheet
, TRUE
, FALSE
);
2430 sheet_style_get_extent (sheet
, &print_area
);
2437 gboolean ignore_strings
;
2440 /* find the maximum width in a range. */
2442 cb_max_cell_width (GnmCellIter
const *iter
, struct cb_fit
*data
)
2445 GnmCell
*cell
= iter
->cell
;
2446 GnmRenderedValue
*rv
;
2448 if (gnm_cell_is_merged (cell
))
2452 * Special handling for manual recalc. We need to eval newly
2453 * entered expressions. gnm_cell_render_value will do that for us,
2454 * but we want to short-circuit some strings early.
2456 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
)
2457 gnm_cell_eval (cell
);
2459 if (data
->ignore_strings
&& VALUE_IS_STRING (cell
->value
))
2462 /* Variable width cell must be re-rendered */
2463 rv
= gnm_cell_get_rendered_value (cell
);
2464 if (rv
== NULL
|| rv
->variable_width
)
2465 gnm_cell_render_value (cell
, FALSE
);
2467 /* Make sure things are as-if drawn. */
2468 cell_finish_layout (cell
, NULL
, iter
->ci
->size_pixels
, TRUE
);
2470 width
= gnm_cell_rendered_width (cell
) + gnm_cell_rendered_offset (cell
);
2471 if (width
> data
->max
)
2478 * sheet_col_size_fit_pixels:
2480 * @col: the column that we want to query
2481 * @srow: starting row.
2482 * @erow: ending row.
2483 * @ignore_strings: skip cells containing string values.
2485 * This routine computes the ideal size for the column to make the contents all
2486 * cells in the column visible.
2488 * Returns: Maximum size in pixels INCLUDING margins and grid lines
2489 * or 0 if there are no cells.
2492 sheet_col_size_fit_pixels (Sheet
*sheet
, int col
, int srow
, int erow
,
2493 gboolean ignore_strings
)
2496 ColRowInfo
*ci
= sheet_col_get (sheet
, col
);
2501 data
.ignore_strings
= ignore_strings
;
2502 sheet_foreach_cell_in_range (sheet
,
2503 CELL_ITER_IGNORE_NONEXISTENT
|
2504 CELL_ITER_IGNORE_HIDDEN
|
2505 CELL_ITER_IGNORE_FILTERED
,
2506 col
, srow
, col
, erow
,
2507 (CellIterFunc
)&cb_max_cell_width
, &data
);
2509 /* Reset to the default width if the column was empty */
2513 /* GnmCell width does not include margins or far grid line*/
2514 return data
.max
+ GNM_COL_MARGIN
+ GNM_COL_MARGIN
+ 1;
2517 /* find the maximum height in a range. */
2519 cb_max_cell_height (GnmCellIter
const *iter
, struct cb_fit
*data
)
2522 GnmCell
*cell
= iter
->cell
;
2524 if (gnm_cell_is_merged (cell
))
2528 * Special handling for manual recalc. We need to eval newly
2529 * entered expressions. gnm_cell_render_value will do that for us,
2530 * but we want to short-circuit some strings early.
2532 if (cell
->base
.flags
& GNM_CELL_HAS_NEW_EXPR
)
2533 gnm_cell_eval (cell
);
2535 if (data
->ignore_strings
&& VALUE_IS_STRING (cell
->value
))
2538 if (!VALUE_IS_STRING (cell
->value
)) {
2540 * Mildly cheating to avoid performance problems, See bug
2541 * 359392. This assumes that non-strings do not wrap and
2542 * that they are all the same height, more or less.
2544 Sheet
const *sheet
= cell
->base
.sheet
;
2545 height
= gnm_style_get_pango_height (gnm_cell_get_style (cell
),
2546 sheet
->rendered_values
->context
,
2547 sheet
->last_zoom_factor_used
);
2549 (void)gnm_cell_fetch_rendered_value (cell
, TRUE
);
2551 /* Make sure things are as-if drawn. Inhibit #####s. */
2552 cell_finish_layout (cell
, NULL
, iter
->ci
->size_pixels
, FALSE
);
2554 height
= gnm_cell_rendered_height (cell
);
2557 if (height
> data
->max
)
2564 * sheet_row_size_fit_pixels:
2566 * @row: the row that we want to query
2567 * @scol: starting column.
2568 * @ecol: ending column.
2569 * @ignore_strings: skip cells containing string values.
2571 * This routine computes the ideal size for the row to make all data fit
2574 * Returns: Maximum size in pixels INCLUDING margins and grid lines
2575 * or 0 if there are no cells.
2578 sheet_row_size_fit_pixels (Sheet
*sheet
, int row
, int scol
, int ecol
,
2579 gboolean ignore_strings
)
2582 ColRowInfo
const *ri
= sheet_row_get (sheet
, row
);
2587 data
.ignore_strings
= ignore_strings
;
2588 sheet_foreach_cell_in_range (sheet
,
2589 CELL_ITER_IGNORE_NONEXISTENT
|
2590 CELL_ITER_IGNORE_HIDDEN
|
2591 CELL_ITER_IGNORE_FILTERED
,
2594 (CellIterFunc
)&cb_max_cell_height
, &data
);
2596 /* Reset to the default width if the column was empty */
2600 /* GnmCell height does not include margins or bottom grid line */
2601 return data
.max
+ GNM_ROW_MARGIN
+ GNM_ROW_MARGIN
+ 1;
2604 struct recalc_span_closure
{
2610 cb_recalc_spans_in_col (GnmColRowIter
const *iter
, gpointer user
)
2612 struct recalc_span_closure
*closure
= user
;
2613 int const col
= closure
->col
;
2615 CellSpanInfo
const *span
= row_span_get (iter
->cri
, col
);
2618 /* If there is an existing span see if it changed */
2619 GnmCell
const * const cell
= span
->cell
;
2620 cell_calc_span (cell
, &left
, &right
);
2621 if (left
!= span
->left
|| right
!= span
->right
) {
2622 cell_unregister_span (cell
);
2623 cell_register_span (cell
, left
, right
);
2626 /* If there is a cell see if it started to span */
2627 GnmCell
const * const cell
= sheet_cell_get (closure
->sheet
, col
, iter
->pos
);
2629 cell_calc_span (cell
, &left
, &right
);
2631 cell_register_span (cell
, left
, right
);
2639 * sheet_recompute_spans_for_col:
2641 * @col: The column that changed
2643 * This routine recomputes the column span for the cells that touches
2647 sheet_recompute_spans_for_col (Sheet
*sheet
, int col
)
2649 struct recalc_span_closure closure
;
2650 closure
.sheet
= sheet
;
2653 col_row_collection_foreach (&sheet
->rows
, 0, gnm_sheet_get_last_row (sheet
),
2654 &cb_recalc_spans_in_col
, &closure
);
2657 /****************************************************************************/
2660 GnmExprTop
const *texpr
;
2661 GnmRange expr_bound
;
2662 } closure_set_cell_value
;
2665 cb_set_cell_content (GnmCellIter
const *iter
, closure_set_cell_value
*info
)
2667 GnmExprTop
const *texpr
= info
->texpr
;
2672 cell
= sheet_cell_create (iter
->pp
.sheet
,
2677 * If we are overwriting an array, we need to clear things here
2678 * or gnm_cell_set_expr/gnm_cell_set_value will complain.
2680 if (cell
->base
.texpr
&& gnm_expr_top_is_array (cell
->base
.texpr
))
2681 gnm_cell_cleanout (cell
);
2683 if (texpr
!= NULL
) {
2684 if (!range_contains (&info
->expr_bound
,
2685 iter
->pp
.eval
.col
, iter
->pp
.eval
.row
)) {
2686 GnmExprRelocateInfo rinfo
;
2688 rinfo
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
2689 rinfo
.pos
= iter
->pp
;
2690 rinfo
.origin
.start
= iter
->pp
.eval
;
2691 rinfo
.origin
.end
= iter
->pp
.eval
;
2692 rinfo
.origin_sheet
= iter
->pp
.sheet
;
2693 rinfo
.target_sheet
= iter
->pp
.sheet
;
2694 rinfo
.col_offset
= 0;
2695 rinfo
.row_offset
= 0;
2696 texpr
= gnm_expr_top_relocate (texpr
, &rinfo
, FALSE
);
2699 gnm_cell_set_expr (cell
, texpr
);
2701 gnm_cell_set_value (cell
, value_dup (info
->val
));
2706 cb_clear_non_corner (GnmCellIter
const *iter
, GnmRange
const *merged
)
2708 if (merged
->start
.col
!= iter
->pp
.eval
.col
||
2709 merged
->start
.row
!= iter
->pp
.eval
.row
)
2710 gnm_cell_set_value (iter
->cell
, value_new_empty ());
2715 * sheet_range_set_expr_cb:
2716 * @sr: #GnmSheetRange
2717 * @texpr: #GnmExprTop
2720 * Does NOT check for array division.
2723 sheet_range_set_expr_cb (GnmSheetRange
const *sr
, GnmExprTop
const *texpr
)
2725 closure_set_cell_value closure
;
2726 GSList
*merged
, *ptr
;
2728 g_return_if_fail (sr
!= NULL
);
2729 g_return_if_fail (texpr
!= NULL
);
2731 closure
.texpr
= texpr
;
2732 gnm_expr_top_get_boundingbox (closure
.texpr
,
2734 &closure
.expr_bound
);
2736 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2737 /* Store the parsed result creating any cells necessary */
2738 sheet_foreach_cell_in_range
2739 (sr
->sheet
, CELL_ITER_ALL
,
2740 sr
->range
.start
.col
, sr
->range
.start
.row
,
2741 sr
->range
.end
.col
, sr
->range
.end
.row
,
2742 (CellIterFunc
)&cb_set_cell_content
, &closure
);
2744 merged
= gnm_sheet_merge_get_overlap (sr
->sheet
, &sr
->range
);
2745 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
2746 GnmRange
const *tmp
= ptr
->data
;
2747 sheet_foreach_cell_in_range
2748 (sr
->sheet
, CELL_ITER_IGNORE_BLANK
,
2749 tmp
->start
.col
, tmp
->start
.row
,
2750 tmp
->end
.col
, tmp
->end
.row
,
2751 (CellIterFunc
)&cb_clear_non_corner
,
2754 g_slist_free (merged
);
2756 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2757 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2758 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2763 * sheet_range_set_expr_undo:
2764 * @sr: #GnmSheetRange
2765 * @texpr: #GnmExprTop
2767 * Returns: (transfer full): the newly created #GOUndo.
2770 sheet_range_set_expr_undo (GnmSheetRange
*sr
, GnmExprTop
const *texpr
)
2772 gnm_expr_top_ref (texpr
);
2773 return go_undo_binary_new
2774 (sr
, (gpointer
)texpr
,
2775 (GOUndoBinaryFunc
) sheet_range_set_expr_cb
,
2776 (GFreeFunc
) gnm_sheet_range_free
,
2777 (GFreeFunc
) gnm_expr_top_unref
);
2782 * sheet_range_set_text:
2783 * @pos: The position from which to parse an expression.
2784 * @r: The range to fill
2785 * @str: The text to be parsed and assigned.
2787 * Does NOT check for array division.
2789 * Does NOT generate spans.
2792 sheet_range_set_text (GnmParsePos
const *pos
, GnmRange
const *r
, char const *str
)
2794 closure_set_cell_value closure
;
2795 GSList
*merged
, *ptr
;
2798 g_return_if_fail (pos
!= NULL
);
2799 g_return_if_fail (r
!= NULL
);
2800 g_return_if_fail (str
!= NULL
);
2804 parse_text_value_or_expr (pos
, str
,
2805 &closure
.val
, &closure
.texpr
);
2808 gnm_expr_top_get_boundingbox (closure
.texpr
,
2810 &closure
.expr_bound
);
2812 /* Store the parsed result creating any cells necessary */
2813 sheet_foreach_cell_in_range (sheet
, CELL_ITER_ALL
,
2814 r
->start
.col
, r
->start
.row
, r
->end
.col
, r
->end
.row
,
2815 (CellIterFunc
)&cb_set_cell_content
, &closure
);
2817 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
2818 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
2819 GnmRange
const *tmp
= ptr
->data
;
2820 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
,
2821 tmp
->start
.col
, tmp
->start
.row
,
2822 tmp
->end
.col
, tmp
->end
.row
,
2823 (CellIterFunc
)&cb_clear_non_corner
, (gpointer
)tmp
);
2825 g_slist_free (merged
);
2827 sheet_region_queue_recalc (sheet
, r
);
2829 value_release (closure
.val
);
2831 gnm_expr_top_unref (closure
.texpr
);
2833 sheet_flag_status_update_range (sheet
, r
);
2837 sheet_range_set_text_cb (GnmSheetRange
const *sr
, gchar
const *text
)
2841 pos
.eval
= sr
->range
.start
;
2842 pos
.sheet
= sr
->sheet
;
2843 pos
.wb
= sr
->sheet
->workbook
;
2845 sheet_range_set_text (&pos
, &sr
->range
, text
);
2846 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2847 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2848 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2850 sheet_redraw_range (sr
->sheet
, &sr
->range
);
2854 * sheet_range_set_text_undo:
2855 * @sr: #GnmSheetRange
2858 * Returns: (transfer full): the newly created #GOUndo.
2861 sheet_range_set_text_undo (GnmSheetRange
*sr
,
2864 return go_undo_binary_new
2865 (sr
, g_strdup (text
),
2866 (GOUndoBinaryFunc
) sheet_range_set_text_cb
,
2867 (GFreeFunc
) gnm_sheet_range_free
,
2868 (GFreeFunc
) g_free
);
2873 cb_set_markup (GnmCellIter
const *iter
, PangoAttrList
*markup
)
2881 if (VALUE_IS_STRING (cell
->value
)) {
2883 GnmValue
*val
= value_dup (cell
->value
);
2885 fmt
= go_format_new_markup (markup
, TRUE
);
2886 value_set_fmt (val
, fmt
);
2887 go_format_unref (fmt
);
2889 gnm_cell_cleanout (cell
);
2890 gnm_cell_assign_value (cell
, val
);
2896 sheet_range_set_markup_cb (GnmSheetRange
const *sr
, PangoAttrList
*markup
)
2898 sheet_foreach_cell_in_range
2899 (sr
->sheet
, CELL_ITER_ALL
,
2900 sr
->range
.start
.col
, sr
->range
.start
.row
,
2901 sr
->range
.end
.col
, sr
->range
.end
.row
,
2902 (CellIterFunc
)&cb_set_markup
, markup
);
2904 sheet_region_queue_recalc (sr
->sheet
, &sr
->range
);
2905 sheet_flag_status_update_range (sr
->sheet
, &sr
->range
);
2906 sheet_queue_respan (sr
->sheet
, sr
->range
.start
.row
,
2911 * sheet_range_set_markup_undo:
2912 * @sr: #GnmSheetRange
2913 * @markup: #PangoAttrList
2915 * Returns: (transfer full): the newly created #GOUndo.
2918 sheet_range_set_markup_undo (GnmSheetRange
*sr
, PangoAttrList
*markup
)
2922 return go_undo_binary_new
2923 (sr
, pango_attr_list_ref (markup
),
2924 (GOUndoBinaryFunc
) sheet_range_set_markup_cb
,
2925 (GFreeFunc
) gnm_sheet_range_free
,
2926 (GFreeFunc
) pango_attr_list_unref
);
2930 * sheet_cell_get_value:
2932 * @col: Source column
2935 * Returns: (transfer none) (nullable): the cell's current value.
2938 sheet_cell_get_value (Sheet
*sheet
, int const col
, int const row
)
2942 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2944 cell
= sheet_cell_get (sheet
, col
, row
);
2946 return cell
? cell
->value
: NULL
;
2950 * sheet_cell_set_text:
2952 * @str: the text to set.
2953 * @markup: (allow-none): an optional PangoAttrList.
2955 * Marks the sheet as dirty
2957 * Flags status updates
2961 sheet_cell_set_text (GnmCell
*cell
, char const *text
, PangoAttrList
*markup
)
2963 GnmExprTop
const *texpr
;
2967 g_return_if_fail (cell
!= NULL
);
2968 g_return_if_fail (text
!= NULL
);
2969 g_return_if_fail (!gnm_cell_is_nonsingleton_array (cell
));
2971 parse_text_value_or_expr (parse_pos_init_cell (&pp
, cell
),
2972 text
, &val
, &texpr
);
2974 /* Queue a redraw before in case the span changes */
2975 sheet_redraw_cell (cell
);
2977 if (texpr
!= NULL
) {
2978 gnm_cell_set_expr (cell
, texpr
);
2979 gnm_expr_top_unref (texpr
);
2982 * Queue recalc before spanning. Otherwise spanning may
2983 * create a bogus rendered value, see #495879.
2985 cell_queue_recalc (cell
);
2987 /* Clear spans from _other_ cells */
2988 sheet_cell_calc_span (cell
, GNM_SPANCALC_SIMPLE
);
2990 g_return_if_fail (val
!= NULL
);
2992 if (markup
!= NULL
&& VALUE_IS_STRING (val
)) {
2993 gboolean quoted
= (text
[0] == '\'');
2994 PangoAttrList
*adj_markup
;
2998 /* We ate the quote. Adjust. Ugh. */
2999 adj_markup
= pango_attr_list_copy (markup
);
3000 go_pango_attr_list_erase (adj_markup
, 0, 1);
3002 adj_markup
= markup
;
3004 fmt
= go_format_new_markup (adj_markup
, TRUE
);
3005 value_set_fmt (val
, fmt
);
3006 go_format_unref (fmt
);
3008 pango_attr_list_unref (adj_markup
);
3011 gnm_cell_set_value (cell
, val
);
3013 /* Queue recalc before spanning, see above. */
3014 cell_queue_recalc (cell
);
3016 sheet_cell_calc_span (cell
, GNM_SPANCALC_RESIZE
| GNM_SPANCALC_RENDER
);
3019 sheet_flag_status_update_cell (cell
);
3023 * sheet_cell_set_text_gi: (rename-to sheet_cell_set_text)
3025 * @col: column number
3027 * @str: the text to set.
3029 * Sets the contents of a cell.
3032 sheet_cell_set_text_gi (Sheet
*sheet
, int col
, int row
, char const *str
)
3034 sheet_cell_set_text (sheet_cell_fetch (sheet
, col
, row
), str
, NULL
);
3039 * sheet_cell_set_expr:
3041 * Marks the sheet as dirty
3043 * Flags status updates
3047 sheet_cell_set_expr (GnmCell
*cell
, GnmExprTop
const *texpr
)
3049 gnm_cell_set_expr (cell
, texpr
);
3051 /* clear spans from _other_ cells */
3052 sheet_cell_calc_span (cell
, GNM_SPANCALC_SIMPLE
);
3054 cell_queue_recalc (cell
);
3055 sheet_flag_status_update_cell (cell
);
3059 * sheet_cell_set_value: (skip)
3061 * @v: (transfer full): #GnmValue
3063 * Stores, without copying, the supplied value. It marks the
3066 * The value is rendered and spans are calculated. It queues a redraw
3067 * and checks to see if the edit region or selection content changed.
3069 * NOTE : This DOES check for array partitioning.
3072 sheet_cell_set_value (GnmCell
*cell
, GnmValue
*v
)
3074 /* TODO : if the value is unchanged do not assign it */
3075 gnm_cell_set_value (cell
, v
);
3076 sheet_cell_calc_span (cell
, GNM_SPANCALC_RESIZE
| GNM_SPANCALC_RENDER
);
3077 cell_queue_recalc (cell
);
3078 sheet_flag_status_update_cell (cell
);
3082 * sheet_cell_set_value_gi: (rename-to sheet_cell_set_value)
3084 * @col: column number
3088 * Set the the value of the cell at (@col,@row) to @v.
3090 * The value is rendered and spans are calculated. It queues a redraw
3091 * and checks to see if the edit region or selection content changed.
3094 sheet_cell_set_value_gi (Sheet
*sheet
, int col
, int row
, GnmValue
*v
)
3096 // This version exists because not all versions of pygobject
3097 // understand transfer-full parameters
3098 sheet_cell_set_value (sheet_cell_fetch (sheet
, col
, row
),
3102 /****************************************************************************/
3105 * This routine is used to queue the redraw regions for the
3106 * cell region specified.
3108 * It is usually called before a change happens to a region,
3109 * and after the change has been done to queue the regions
3110 * for the old contents and the new contents.
3112 * It intelligently handles spans and merged ranges
3115 sheet_range_bounding_box (Sheet
const *sheet
, GnmRange
*bound
)
3119 GnmRange r
= *bound
;
3121 g_return_if_fail (range_is_sane (bound
));
3123 /* Check the first and last columns for spans and extend the region to
3124 * include the maximum extent.
3126 for (row
= r
.start
.row
; row
<= r
.end
.row
; row
++){
3127 ColRowInfo
const *ri
= sheet_row_get (sheet
, row
);
3130 CellSpanInfo
const * span0
;
3132 if (ri
->needs_respan
)
3133 row_calc_spans ((ColRowInfo
*)ri
, row
, sheet
);
3135 span0
= row_span_get (ri
, r
.start
.col
);
3137 if (span0
!= NULL
) {
3138 if (bound
->start
.col
> span0
->left
)
3139 bound
->start
.col
= span0
->left
;
3140 if (bound
->end
.col
< span0
->right
)
3141 bound
->end
.col
= span0
->right
;
3143 if (r
.start
.col
!= r
.end
.col
) {
3144 CellSpanInfo
const * span1
=
3145 row_span_get (ri
, r
.end
.col
);
3147 if (span1
!= NULL
) {
3148 if (bound
->start
.col
> span1
->left
)
3149 bound
->start
.col
= span1
->left
;
3150 if (bound
->end
.col
< span1
->right
)
3151 bound
->end
.col
= span1
->right
;
3154 /* skip segments with no cells */
3155 } else if (row
== COLROW_SEGMENT_START (row
)) {
3156 ColRowSegment
const * const segment
=
3157 COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
3158 if (segment
== NULL
)
3159 row
= COLROW_SEGMENT_END (row
);
3163 /* TODO : this may get expensive if there are alot of merged ranges */
3164 /* no need to iterate, one pass is enough */
3165 for (ptr
= sheet
->list_merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3166 GnmRange
const * const test
= ptr
->data
;
3167 if (r
.start
.row
<= test
->end
.row
|| r
.end
.row
>= test
->start
.row
) {
3168 if (bound
->start
.col
> test
->start
.col
)
3169 bound
->start
.col
= test
->start
.col
;
3170 if (bound
->end
.col
< test
->end
.col
)
3171 bound
->end
.col
= test
->end
.col
;
3172 if (bound
->start
.row
> test
->start
.row
)
3173 bound
->start
.row
= test
->start
.row
;
3174 if (bound
->end
.row
< test
->end
.row
)
3175 bound
->end
.row
= test
->end
.row
;
3181 sheet_redraw_region (Sheet
const *sheet
,
3182 int start_col
, int start_row
,
3183 int end_col
, int end_row
)
3187 g_return_if_fail (IS_SHEET (sheet
));
3190 * Getting the bounding box causes row respans to be done if
3191 * needed. That can be expensive, so just redraw the whole
3192 * sheet if the row count is too big.
3194 if (end_row
- start_row
> 500) {
3195 sheet_redraw_all (sheet
, FALSE
);
3199 /* We potentially do a lot of recalcs as part of this, so make sure
3200 stuff that caches sub-computations see the whole thing instead
3201 of clearing between cells. */
3202 gnm_app_recalc_start ();
3204 sheet_range_bounding_box (sheet
,
3205 range_init (&bound
, start_col
, start_row
, end_col
, end_row
));
3206 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
3207 sc_redraw_range (control
, &bound
););
3209 gnm_app_recalc_finish ();
3213 sheet_redraw_range (Sheet
const *sheet
, GnmRange
const *range
)
3215 g_return_if_fail (IS_SHEET (sheet
));
3216 g_return_if_fail (range
!= NULL
);
3218 sheet_redraw_region (sheet
,
3219 range
->start
.col
, range
->start
.row
,
3220 range
->end
.col
, range
->end
.row
);
3223 /****************************************************************************/
3226 sheet_col_is_hidden (Sheet
const *sheet
, int col
)
3228 ColRowInfo
const * const res
= sheet_col_get (sheet
, col
);
3229 return (res
!= NULL
&& !res
->visible
);
3233 sheet_row_is_hidden (Sheet
const *sheet
, int row
)
3235 ColRowInfo
const * const res
= sheet_row_get (sheet
, row
);
3236 return (res
!= NULL
&& !res
->visible
);
3241 * sheet_find_boundary_horizontal:
3243 * @col: The column from which to begin searching.
3244 * @move_row: The row in which to search for the edge of the range.
3245 * @base_row: The height of the area being moved.
3246 * @count: units to extend the selection vertically
3247 * @jump_to_boundaries: Jump to range boundaries.
3249 * Calculate the column index for the column which is @n units
3250 * from @start_col doing bounds checking. If @jump_to_boundaries is
3251 * TRUE @n must be 1 and the jump is to the edge of the logical range.
3253 * NOTE : This routine implements the logic necasary for ctrl-arrow style
3254 * movement. That is more compilcated than simply finding the last in a list
3255 * of cells with content. If you are at the end of a range it will find the
3256 * start of the next. Make sure that is the sort of behavior you want before
3258 * Returns: the column inex.
3261 sheet_find_boundary_horizontal (Sheet
*sheet
, int start_col
, int move_row
,
3262 int base_row
, int count
,
3263 gboolean jump_to_boundaries
)
3265 gboolean find_nonblank
= sheet_is_cell_empty (sheet
, start_col
, move_row
);
3266 gboolean keep_looking
= FALSE
;
3267 int new_col
, prev_col
, lagged_start_col
, max_col
= gnm_sheet_get_last_col (sheet
);
3269 GnmRange check_merge
;
3270 GnmRange
const * const bound
= &sheet
->priv
->unhidden_region
;
3272 /* Jumping to bounds requires steping cell by cell */
3273 g_return_val_if_fail (count
== 1 || count
== -1 || !jump_to_boundaries
, start_col
);
3274 g_return_val_if_fail (IS_SHEET (sheet
), start_col
);
3276 if (move_row
< base_row
) {
3277 check_merge
.start
.row
= move_row
;
3278 check_merge
.end
.row
= base_row
;
3280 check_merge
.end
.row
= move_row
;
3281 check_merge
.start
.row
= base_row
;
3285 GSList
*merged
, *ptr
;
3287 lagged_start_col
= check_merge
.start
.col
= check_merge
.end
.col
= start_col
;
3288 merged
= gnm_sheet_merge_get_overlap (sheet
, &check_merge
);
3289 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3290 GnmRange
const * const r
= ptr
->data
;
3292 if (start_col
< r
->end
.col
)
3293 start_col
= r
->end
.col
;
3295 if (start_col
> r
->start
.col
)
3296 start_col
= r
->start
.col
;
3299 g_slist_free (merged
);
3300 } while (start_col
!= lagged_start_col
);
3301 new_col
= prev_col
= start_col
;
3307 if (new_col
< bound
->start
.col
)
3308 return MIN (bound
->start
.col
, max_col
);
3309 if (new_col
> bound
->end
.col
)
3310 return MIN (bound
->end
.col
, max_col
);
3312 keep_looking
= sheet_col_is_hidden (sheet
, new_col
);
3313 if (jump_to_boundaries
) {
3314 if (new_col
> sheet
->cols
.max_used
) {
3316 return (find_nonblank
|| iterations
== 1)?
3317 MIN (bound
->end
.col
, max_col
):
3318 MIN (prev_col
, max_col
);
3319 new_col
= sheet
->cols
.max_used
;
3322 keep_looking
|= (sheet_is_cell_empty (sheet
, new_col
, move_row
) == find_nonblank
);
3325 else if (!find_nonblank
) {
3327 * Handle special case where we are on the last
3330 if (iterations
== 1)
3331 keep_looking
= find_nonblank
= TRUE
;
3336 } while (keep_looking
);
3338 return MIN (new_col
, max_col
);
3342 * sheet_find_boundary_vertical:
3343 * @sheet: The Sheet *
3344 * @move_col: The col in which to search for the edge of the range.
3345 * @row: The row from which to begin searching.
3346 * @base_col: The width of the area being moved.
3347 * @count: units to extend the selection vertically
3348 * @jump_to_boundaries: Jump to range boundaries.
3350 * Calculate the row index for the row which is @n units
3351 * from @start_row doing bounds checking. If @jump_to_boundaries is
3352 * TRUE @n must be 1 and the jump is to the edge of the logical range.
3354 * NOTE : This routine implements the logic necasary for ctrl-arrow style
3355 * movement. That is more compilcated than simply finding the last in a list
3356 * of cells with content. If you are at the end of a range it will find the
3357 * start of the next. Make sure that is the sort of behavior you want before
3359 * Returns: the row index.
3362 sheet_find_boundary_vertical (Sheet
*sheet
, int move_col
, int start_row
,
3363 int base_col
, int count
,
3364 gboolean jump_to_boundaries
)
3366 gboolean find_nonblank
= sheet_is_cell_empty (sheet
, move_col
, start_row
);
3367 gboolean keep_looking
= FALSE
;
3368 int new_row
, prev_row
, lagged_start_row
, max_row
= gnm_sheet_get_last_row (sheet
);
3370 GnmRange check_merge
;
3371 GnmRange
const * const bound
= &sheet
->priv
->unhidden_region
;
3373 /* Jumping to bounds requires steping cell by cell */
3374 g_return_val_if_fail (count
== 1 || count
== -1 || !jump_to_boundaries
, start_row
);
3375 g_return_val_if_fail (IS_SHEET (sheet
), start_row
);
3377 if (move_col
< base_col
) {
3378 check_merge
.start
.col
= move_col
;
3379 check_merge
.end
.col
= base_col
;
3381 check_merge
.end
.col
= move_col
;
3382 check_merge
.start
.col
= base_col
;
3386 GSList
*merged
, *ptr
;
3388 lagged_start_row
= check_merge
.start
.row
= check_merge
.end
.row
= start_row
;
3389 merged
= gnm_sheet_merge_get_overlap (sheet
, &check_merge
);
3390 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3391 GnmRange
const * const r
= ptr
->data
;
3393 if (start_row
< r
->end
.row
)
3394 start_row
= r
->end
.row
;
3396 if (start_row
> r
->start
.row
)
3397 start_row
= r
->start
.row
;
3400 g_slist_free (merged
);
3401 } while (start_row
!= lagged_start_row
);
3402 new_row
= prev_row
= start_row
;
3408 if (new_row
< bound
->start
.row
)
3409 return MIN (bound
->start
.row
, max_row
);
3410 if (new_row
> bound
->end
.row
)
3411 return MIN (bound
->end
.row
, max_row
);
3413 keep_looking
= sheet_row_is_hidden (sheet
, new_row
);
3414 if (jump_to_boundaries
) {
3415 if (new_row
> sheet
->rows
.max_used
) {
3417 return (find_nonblank
|| iterations
== 1)?
3418 MIN (bound
->end
.row
, max_row
):
3419 MIN (prev_row
, max_row
);
3420 new_row
= sheet
->rows
.max_used
;
3423 keep_looking
|= (sheet_is_cell_empty (sheet
, move_col
, new_row
) == find_nonblank
);
3426 else if (!find_nonblank
) {
3428 * Handle special case where we are on the last
3431 if (iterations
== 1)
3432 keep_looking
= find_nonblank
= TRUE
;
3437 } while (keep_looking
);
3439 return MIN (new_row
, max_row
);
3443 CHECK_AND_LOAD_START
= 1,
3452 GnmRange
const *ignore
;
3458 cb_check_array_horizontal (GnmColRowIter
const *iter
, ArrayCheckData
*data
)
3460 gboolean is_array
= FALSE
;
3462 if (data
->flags
& CHECK_AND_LOAD_START
&& /* Top */
3463 (is_array
= gnm_cell_array_bound (
3464 sheet_cell_get (data
->sheet
, iter
->pos
, data
->start
),
3466 data
->error
.start
.row
< data
->start
&&
3467 (data
->ignore
== NULL
||
3468 !range_contained (&data
->error
, data
->ignore
)))
3471 if (data
->flags
& LOAD_END
)
3472 is_array
= gnm_cell_array_bound (
3473 sheet_cell_get (data
->sheet
, iter
->pos
, data
->end
),
3476 return (data
->flags
& CHECK_END
&&
3478 data
->error
.end
.row
> data
->end
&& /* Bottom */
3479 (data
->ignore
== NULL
||
3480 !range_contained (&data
->error
, data
->ignore
)));
3484 cb_check_array_vertical (GnmColRowIter
const *iter
, ArrayCheckData
*data
)
3486 gboolean is_array
= FALSE
;
3488 if (data
->flags
& CHECK_AND_LOAD_START
&& /* Left */
3489 (is_array
= gnm_cell_array_bound (
3490 sheet_cell_get (data
->sheet
, data
->start
, iter
->pos
),
3492 data
->error
.start
.col
< 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
, data
->end
, iter
->pos
),
3502 return (data
->flags
& CHECK_END
&&
3504 data
->error
.end
.col
> data
->end
&& /* Right */
3505 (data
->ignore
== NULL
||
3506 !range_contained (&data
->error
, data
->ignore
)));
3510 * sheet_range_splits_array:
3511 * @sheet: The sheet.
3512 * @r: The range to check
3513 * @ignore: (nullable): a range in which it is ok to have an array.
3514 * @cc: (nullable): place to report an error.
3515 * @cmd: (nullable): cmd name used with @cc.
3517 * Check the outer edges of range @sheet!@r to ensure that if an array is
3518 * within it then the entire array is within the range. @ignore is useful when
3519 * src & dest ranges may overlap.
3521 * Returns: TRUE if an array would be split.
3524 sheet_range_splits_array (Sheet
const *sheet
,
3525 GnmRange
const *r
, GnmRange
const *ignore
,
3526 GOCmdContext
*cc
, char const *cmd
)
3528 ArrayCheckData closure
;
3530 g_return_val_if_fail (r
->start
.col
<= r
->end
.col
, FALSE
);
3531 g_return_val_if_fail (r
->start
.row
<= r
->end
.row
, FALSE
);
3533 closure
.sheet
= sheet
;
3534 closure
.ignore
= ignore
;
3536 closure
.start
= r
->start
.row
;
3537 closure
.end
= r
->end
.row
;
3538 if (closure
.start
<= 0) {
3539 closure
.flags
= (closure
.end
< sheet
->rows
.max_used
)
3540 ? CHECK_END
| LOAD_END
3542 } else if (closure
.end
< sheet
->rows
.max_used
)
3543 closure
.flags
= (closure
.start
== closure
.end
)
3544 ? CHECK_AND_LOAD_START
| CHECK_END
3545 : CHECK_AND_LOAD_START
| CHECK_END
| LOAD_END
;
3547 closure
.flags
= CHECK_AND_LOAD_START
;
3549 if (closure
.flags
&&
3550 col_row_collection_foreach (&sheet
->cols
, r
->start
.col
, r
->end
.col
,
3551 (ColRowHandler
) cb_check_array_horizontal
, &closure
)) {
3553 gnm_cmd_context_error_splits_array (cc
,
3554 cmd
, &closure
.error
);
3558 closure
.start
= r
->start
.col
;
3559 closure
.end
= r
->end
.col
;
3560 if (closure
.start
<= 0) {
3561 closure
.flags
= (closure
.end
< sheet
->cols
.max_used
)
3562 ? CHECK_END
| LOAD_END
3564 } else if (closure
.end
< sheet
->cols
.max_used
)
3565 closure
.flags
= (closure
.start
== closure
.end
)
3566 ? CHECK_AND_LOAD_START
| CHECK_END
3567 : CHECK_AND_LOAD_START
| CHECK_END
| LOAD_END
;
3569 closure
.flags
= CHECK_AND_LOAD_START
;
3571 if (closure
.flags
&&
3572 col_row_collection_foreach (&sheet
->rows
, r
->start
.row
, r
->end
.row
,
3573 (ColRowHandler
) cb_check_array_vertical
, &closure
)) {
3575 gnm_cmd_context_error_splits_array (cc
,
3576 cmd
, &closure
.error
);
3583 * sheet_range_splits_region:
3584 * @sheet: the sheet.
3585 * @r: The range whose boundaries are checked
3586 * @ignore: An optional range in which it is ok to have arrays and merges
3587 * @cc: The context that issued the command
3588 * @cmd: The translated command name.
3590 * A utility to see whether moving the range @r will split any arrays
3591 * or merged regions.
3592 * Returns: whether any arrays or merged regions will be split.
3595 sheet_range_splits_region (Sheet
const *sheet
,
3596 GnmRange
const *r
, GnmRange
const *ignore
,
3597 GOCmdContext
*cc
, char const *cmd_name
)
3601 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
3603 /* Check for array subdivision */
3604 if (sheet_range_splits_array (sheet
, r
, ignore
, cc
, cmd_name
))
3607 merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
3611 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
) {
3612 GnmRange
const *m
= ptr
->data
;
3613 if (ignore
!= NULL
&& range_contained (m
, ignore
))
3615 if (!range_contained (m
, r
))
3618 g_slist_free (merged
);
3620 if (cc
!= NULL
&& ptr
!= NULL
) {
3621 go_cmd_context_error_invalid (cc
, cmd_name
,
3622 _("Target region contains merged cells"));
3630 * sheet_ranges_split_region:
3631 * @sheet: the sheet.
3632 * @ranges: (element-type GnmRange): A list of ranges to check.
3633 * @cc: The context that issued the command
3634 * @cmd: The translated command name.
3636 * A utility to see whether moving any of the ranges @ranges will split any
3637 * arrays or merged regions.
3638 * Returns: whether any arrays or merged regions will be splitted.
3641 sheet_ranges_split_region (Sheet
const * sheet
, GSList
const *ranges
,
3642 GOCmdContext
*cc
, char const *cmd
)
3646 /* Check for array subdivision */
3647 for (l
= ranges
; l
!= NULL
; l
= l
->next
) {
3648 GnmRange
const *r
= l
->data
;
3649 if (sheet_range_splits_region (sheet
, r
, NULL
, cc
, cmd
))
3656 cb_cell_is_array (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
3658 return gnm_cell_is_array (iter
->cell
) ? VALUE_TERMINATE
: NULL
;
3662 * sheet_range_contains_merges_or_arrays:
3664 * @r: the range to check.
3665 * @cc: an optional place to report errors.
3667 * @merges: if %TRUE, check for merges.
3668 * @arrays: if %TRUE, check for arrays.
3670 * Check to see if the target region @sheet!@r contains any merged regions or
3671 * arrays. Report an error to the @cc if it is supplied.
3672 * Returns: %TRUE if the target region @sheet!@r contains any merged regions or
3676 sheet_range_contains_merges_or_arrays (Sheet
const *sheet
, GnmRange
const *r
,
3677 GOCmdContext
*cc
, char const *cmd
,
3678 gboolean merges
, gboolean arrays
)
3680 g_return_val_if_fail (IS_SHEET (sheet
), FALSE
);
3683 GSList
*merged
= gnm_sheet_merge_get_overlap (sheet
, r
);
3684 if (merged
!= NULL
) {
3686 go_cmd_context_error_invalid
3688 _("cannot operate on merged cells"));
3689 g_slist_free (merged
);
3695 if (sheet_foreach_cell_in_range (
3696 (Sheet
*)sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
3697 r
->start
.col
, r
->start
.row
, r
->end
.col
, r
->end
.row
,
3698 cb_cell_is_array
, NULL
)) {
3700 go_cmd_context_error_invalid
3702 _("cannot operate on array formul\303\246"));
3710 /***************************************************************************/
3713 * sheet_colrow_get_default:
3717 * Returns: (transfer none): the default #ColRowInfo.
3720 sheet_colrow_get_default (Sheet
const *sheet
, gboolean is_cols
)
3722 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3724 return is_cols
? &sheet
->cols
.default_style
: &sheet
->rows
.default_style
;
3728 sheet_colrow_optimize1 (int max
, int max_used
, ColRowCollection
*collection
)
3731 int first_unused
= max_used
+ 1;
3733 for (i
= COLROW_SEGMENT_START (first_unused
);
3735 i
+= COLROW_SEGMENT_SIZE
) {
3736 ColRowSegment
*segment
= COLROW_GET_SEGMENT (collection
, i
);
3738 gboolean any
= FALSE
;
3742 for (j
= 0; j
< COLROW_SEGMENT_SIZE
; j
++) {
3743 ColRowInfo
*info
= segment
->info
[j
];
3746 if (i
+ j
>= first_unused
&&
3747 col_row_info_equal (&collection
->default_style
, info
)) {
3749 segment
->info
[j
] = NULL
;
3752 if (i
+ j
>= first_unused
)
3759 COLROW_GET_SEGMENT (collection
, i
) = NULL
;
3763 collection
->max_used
= max_used
;
3767 sheet_colrow_optimize (Sheet
*sheet
)
3771 g_return_if_fail (IS_SHEET (sheet
));
3773 extent
= sheet_get_cells_extent (sheet
);
3775 sheet_colrow_optimize1 (gnm_sheet_get_max_cols (sheet
),
3778 sheet_colrow_optimize1 (gnm_sheet_get_max_rows (sheet
),
3785 * @col: column number
3787 * Returns: (transfer none) (nullable): A #ColRowInfo for the column, or %NULL
3788 * if none has been allocated yet.
3791 sheet_col_get (Sheet
const *sheet
, int col
)
3793 ColRowSegment
*segment
;
3795 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3796 g_return_val_if_fail (col
< gnm_sheet_get_max_cols (sheet
), NULL
);
3797 g_return_val_if_fail (col
>= 0, NULL
);
3799 segment
= COLROW_GET_SEGMENT (&(sheet
->cols
), col
);
3800 if (segment
!= NULL
)
3801 return segment
->info
[COLROW_SUB_INDEX (col
)];
3809 * Returns: (transfer none) (nullable): A #ColRowInfo for the row, or %NULL
3810 * if none has been allocated yet.
3813 sheet_row_get (Sheet
const *sheet
, int row
)
3815 ColRowSegment
*segment
;
3817 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3818 g_return_val_if_fail (row
< gnm_sheet_get_max_rows (sheet
), NULL
);
3819 g_return_val_if_fail (row
>= 0, NULL
);
3821 segment
= COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
3822 if (segment
!= NULL
)
3823 return segment
->info
[COLROW_SUB_INDEX (row
)];
3828 sheet_colrow_get (Sheet
const *sheet
, int colrow
, gboolean is_cols
)
3831 return sheet_col_get (sheet
, colrow
);
3832 return sheet_row_get (sheet
, colrow
);
3837 * @col: column number
3839 * Returns: (transfer none): The #ColRowInfo for column @col. This result
3840 * will not be the default #ColRowInfo and may be changed.
3843 sheet_col_fetch (Sheet
*sheet
, int pos
)
3845 ColRowInfo
*cri
= sheet_col_get (sheet
, pos
);
3846 if (NULL
== cri
&& NULL
!= (cri
= sheet_col_new (sheet
)))
3847 sheet_colrow_add (sheet
, cri
, TRUE
, pos
);
3855 * Returns: (transfer none): The #ColRowInfo for row @row. This result
3856 * will not be the default #ColRowInfo and may be changed.
3859 sheet_row_fetch (Sheet
*sheet
, int pos
)
3861 ColRowInfo
*cri
= sheet_row_get (sheet
, pos
);
3862 if (NULL
== cri
&& NULL
!= (cri
= sheet_row_new (sheet
)))
3863 sheet_colrow_add (sheet
, cri
, FALSE
, pos
);
3868 sheet_colrow_fetch (Sheet
*sheet
, int colrow
, gboolean is_cols
)
3871 return sheet_col_fetch (sheet
, colrow
);
3872 return sheet_row_fetch (sheet
, colrow
);
3876 * sheet_col_get_info:
3877 * @col: column number
3879 * Returns: (transfer none): The #ColRowInfo for column @col. The may be
3880 * the default #ColRowInfo for columns and should not be changed.
3883 sheet_col_get_info (Sheet
const *sheet
, int col
)
3885 ColRowInfo
*ci
= sheet_col_get (sheet
, col
);
3889 return &sheet
->cols
.default_style
;
3893 * sheet_row_get_info:
3894 * @row: column number
3896 * Returns: (transfer none): The #ColRowInfo for row @row. The may be
3897 * the default #ColRowInfo for rows and should not be changed.
3900 sheet_row_get_info (Sheet
const *sheet
, int row
)
3902 ColRowInfo
*ri
= sheet_row_get (sheet
, row
);
3906 return &sheet
->rows
.default_style
;
3910 sheet_colrow_get_info (Sheet
const *sheet
, int colrow
, gboolean is_cols
)
3913 ? sheet_col_get_info (sheet
, colrow
)
3914 : sheet_row_get_info (sheet
, colrow
);
3917 /*****************************************************************************/
3920 cell_ordering (gconstpointer a_
, gconstpointer b_
)
3922 GnmCell
const *a
= *(GnmCell
**)a_
;
3923 GnmCell
const *b
= *(GnmCell
**)b_
;
3925 if (a
->pos
.row
!= b
->pos
.row
)
3926 return a
->pos
.row
- b
->pos
.row
;
3928 return a
->pos
.col
- b
->pos
.col
;
3934 * @r: (nullable): a #GnmRange
3936 * Retrieves an array of all cells inside @r.
3937 * Returns: (element-type GnmCell) (transfer container): the cells array.
3940 sheet_cells (Sheet
*sheet
, const GnmRange
*r
)
3942 GPtrArray
*res
= g_ptr_array_new ();
3943 GHashTableIter hiter
;
3946 g_hash_table_iter_init (&hiter
, sheet
->cell_hash
);
3947 while (g_hash_table_iter_next (&hiter
, NULL
, &value
)) {
3948 GnmCell
*cell
= value
;
3949 if (!r
|| range_contains (r
, cell
->pos
.col
, cell
->pos
.row
))
3950 g_ptr_array_add (res
, cell
);
3952 g_ptr_array_sort (res
, cell_ordering
);
3959 #define SWAP_INT(a,b) do { int t; t = a; a = b; b = t; } while (0)
3962 * sheet_foreach_cell_in_range:
3969 * @callback: (scope call): #CellFiletrFunc
3970 * @closure: user data.
3972 * For each existing cell in the range specified, invoke the
3973 * callback routine. If the only_existing flag is passed, then
3974 * callbacks are only invoked for existing cells.
3976 * Note: this function does not honour the CELL_ITER_IGNORE_SUBTOTAL flag.
3978 * Returns: (transfer none): the value returned by the callback, which can be:
3979 * non-NULL on error, or VALUE_TERMINATE if some invoked routine requested
3980 * to stop (by returning non-NULL).
3982 * NOTE: between 0.56 and 0.57, the traversal order changed. The order is now
3988 * (This appears to be the order in which XL looks at the values of ranges.)
3989 * If your code depends on any particular ordering, please add a very visible
3990 * comment near the call.
3993 sheet_foreach_cell_in_range (Sheet
*sheet
, CellIterFlags flags
,
3994 int start_col
, int start_row
,
3995 int end_col
, int end_row
,
3996 CellIterFunc callback
, void *closure
)
4000 gboolean
const visibility_matters
= (flags
& CELL_ITER_IGNORE_HIDDEN
) != 0;
4001 gboolean
const ignore_filtered
= (flags
& CELL_ITER_IGNORE_FILTERED
) != 0;
4002 gboolean
const only_existing
= (flags
& CELL_ITER_IGNORE_NONEXISTENT
) != 0;
4003 gboolean
const ignore_empty
= (flags
& CELL_ITER_IGNORE_EMPTY
) != 0;
4005 gboolean use_celllist
;
4008 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
4009 g_return_val_if_fail (callback
!= NULL
, NULL
);
4011 iter
.pp
.sheet
= sheet
;
4012 iter
.pp
.wb
= sheet
->workbook
;
4014 if (start_col
> end_col
)
4015 SWAP_INT (start_col
, end_col
);
4016 if (end_col
< 0 || start_col
> gnm_sheet_get_last_col (sheet
))
4018 start_col
= MAX (0, start_col
);
4019 end_col
= MIN (end_col
, gnm_sheet_get_last_col (sheet
));
4021 if (start_row
> end_row
)
4022 SWAP_INT (start_row
, end_row
);
4023 if (end_row
< 0 || start_row
> gnm_sheet_get_last_row (sheet
))
4025 start_row
= MAX (0, start_row
);
4026 end_row
= MIN (end_row
, gnm_sheet_get_last_row (sheet
));
4028 range_size
= (guint64
)(end_row
- start_row
+ 1) * (end_col
- start_col
+ 1);
4031 range_size
> g_hash_table_size (sheet
->cell_hash
) + 1000;
4033 GPtrArray
*all_cells
;
4034 int last_row
= -1, last_col
= -1;
4035 GnmValue
*res
= NULL
;
4039 if (gnm_debug_flag ("sheet-foreach"))
4040 g_printerr ("Using celllist for area of size %d\n",
4043 range_init (&r
, start_col
, start_row
, end_col
, end_row
);
4044 all_cells
= sheet_cells (sheet
, &r
);
4046 for (ui
= 0; ui
< all_cells
->len
; ui
++) {
4047 GnmCell
*cell
= g_ptr_array_index (all_cells
, ui
);
4050 iter
.pp
.eval
.row
= cell
->pos
.row
;
4051 iter
.pp
.eval
.col
= cell
->pos
.col
;
4053 if (iter
.pp
.eval
.row
!= last_row
) {
4054 last_row
= iter
.pp
.eval
.row
;
4055 iter
.ri
= sheet_row_get (iter
.pp
.sheet
, last_row
);
4057 if (visibility_matters
&& !iter
.ri
->visible
)
4059 if (ignore_filtered
&& iter
.ri
->in_filter
&& !iter
.ri
->visible
)
4062 if (iter
.pp
.eval
.col
!= last_col
) {
4063 last_col
= iter
.pp
.eval
.col
;
4064 iter
.ci
= sheet_col_get (iter
.pp
.sheet
, last_col
);
4066 if (visibility_matters
&& !iter
.ci
->visible
)
4069 ignore
= (ignore_empty
&&
4070 VALUE_IS_EMPTY (cell
->value
) &&
4071 !gnm_cell_needs_recalc (cell
));
4075 res
= (*callback
) (&iter
, closure
);
4080 g_ptr_array_free (all_cells
, TRUE
);
4084 for (iter
.pp
.eval
.row
= start_row
;
4085 iter
.pp
.eval
.row
<= end_row
;
4086 ++iter
.pp
.eval
.row
) {
4087 iter
.ri
= sheet_row_get (iter
.pp
.sheet
, iter
.pp
.eval
.row
);
4089 /* no need to check visibility, that would require a colinfo to exist */
4090 if (iter
.ri
== NULL
) {
4091 if (only_existing
) {
4092 /* skip segments with no cells */
4093 if (iter
.pp
.eval
.row
== COLROW_SEGMENT_START (iter
.pp
.eval
.row
)) {
4094 ColRowSegment
const *segment
=
4095 COLROW_GET_SEGMENT (&(sheet
->rows
), iter
.pp
.eval
.row
);
4096 if (segment
== NULL
)
4097 iter
.pp
.eval
.row
= COLROW_SEGMENT_END (iter
.pp
.eval
.row
);
4101 for (iter
.pp
.eval
.col
= start_col
; iter
.pp
.eval
.col
<= end_col
; ++iter
.pp
.eval
.col
) {
4102 cont
= (*callback
) (&iter
, closure
);
4111 if (visibility_matters
&& !iter
.ri
->visible
)
4113 if (ignore_filtered
&& iter
.ri
->in_filter
&& !iter
.ri
->visible
)
4116 for (iter
.pp
.eval
.col
= start_col
; iter
.pp
.eval
.col
<= end_col
; ++iter
.pp
.eval
.col
) {
4117 iter
.ci
= sheet_col_get (sheet
, iter
.pp
.eval
.col
);
4118 if (iter
.ci
!= NULL
) {
4119 if (visibility_matters
&& !iter
.ci
->visible
)
4121 iter
.cell
= sheet_cell_get (sheet
,
4122 iter
.pp
.eval
.col
, iter
.pp
.eval
.row
);
4126 ignore
= (iter
.cell
== NULL
)
4127 ? (only_existing
|| ignore_empty
)
4128 : (ignore_empty
&& VALUE_IS_EMPTY (iter
.cell
->value
) &&
4129 !gnm_cell_needs_recalc (iter
.cell
));
4132 if (iter
.pp
.eval
.col
== COLROW_SEGMENT_START (iter
.pp
.eval
.col
)) {
4133 ColRowSegment
const *segment
=
4134 COLROW_GET_SEGMENT (&(sheet
->cols
), iter
.pp
.eval
.col
);
4135 if (segment
== NULL
)
4136 iter
.pp
.eval
.col
= COLROW_SEGMENT_END (iter
.pp
.eval
.col
);
4141 cont
= (*callback
) (&iter
, closure
);
4150 * sheet_cell_foreach:
4152 * @callback: (scope call):
4155 * Call @callback with an argument of @data for each cell in the sheet
4158 sheet_cell_foreach (Sheet
const *sheet
, GHFunc callback
, gpointer data
)
4160 g_return_if_fail (IS_SHEET (sheet
));
4162 g_hash_table_foreach (sheet
->cell_hash
, callback
, data
);
4166 * sheet_cells_count:
4169 * Returns the number of cells with content in the current workbook.
4172 sheet_cells_count (Sheet
const *sheet
)
4174 return g_hash_table_size (sheet
->cell_hash
);
4178 cb_sheet_cells_collect (G_GNUC_UNUSED gpointer unused
,
4179 GnmCell
const *cell
,
4182 GnmEvalPos
*ep
= eval_pos_init_cell (g_new (GnmEvalPos
, 1), cell
);
4183 g_ptr_array_add (cells
, ep
);
4187 * sheet_cell_positions:
4188 * @sheet: The sheet to find cells in.
4189 * @comments: If true, include cells with only comments also.
4191 * Collects a GPtrArray of GnmEvalPos pointers for all cells in a sheet.
4192 * No particular order should be assumed.
4193 * Returns: (element-type GnmEvalPos) (transfer full): the newly created array
4196 sheet_cell_positions (Sheet
*sheet
, gboolean comments
)
4198 GPtrArray
*cells
= g_ptr_array_new ();
4200 g_return_val_if_fail (IS_SHEET (sheet
), cells
);
4202 sheet_cell_foreach (sheet
, (GHFunc
)cb_sheet_cells_collect
, cells
);
4206 GSList
*scomments
, *ptr
;
4208 range_init_full_sheet (&r
, sheet
);
4209 scomments
= sheet_objects_get (sheet
, &r
, GNM_CELL_COMMENT_TYPE
);
4210 for (ptr
= scomments
; ptr
; ptr
= ptr
->next
) {
4211 GnmComment
*c
= ptr
->data
;
4212 GnmRange
const *loc
= sheet_object_get_range (GNM_SO (c
));
4213 GnmCell
*cell
= sheet_cell_get (sheet
, loc
->start
.col
, loc
->start
.row
);
4215 /* If cell does not exist, we haven't seen it... */
4216 GnmEvalPos
*ep
= g_new (GnmEvalPos
, 1);
4218 ep
->eval
.col
= loc
->start
.col
;
4219 ep
->eval
.row
= loc
->start
.row
;
4220 g_ptr_array_add (cells
, ep
);
4223 g_slist_free (scomments
);
4231 cb_fail_if_exist (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
4233 return gnm_cell_is_empty (iter
->cell
) ? NULL
: VALUE_TERMINATE
;
4237 * sheet_is_region_empty:
4238 * @sheet: sheet to check
4239 * @r: region to check
4241 * Returns TRUE if the specified region of the @sheet does not
4245 sheet_is_region_empty (Sheet
*sheet
, GnmRange
const *r
)
4247 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
4249 return sheet_foreach_cell_in_range (
4250 sheet
, CELL_ITER_IGNORE_BLANK
,
4251 r
->start
.col
, r
->start
.row
, r
->end
.col
, r
->end
.row
,
4252 cb_fail_if_exist
, NULL
) == NULL
;
4256 sheet_is_cell_empty (Sheet
*sheet
, int col
, int row
)
4258 GnmCell
const *cell
= sheet_cell_get (sheet
, col
, row
);
4259 return gnm_cell_is_empty (cell
);
4263 * sheet_cell_add_to_hash:
4264 * @sheet The sheet where the cell is inserted
4265 * @cell The cell, it should already have col/pos pointers
4266 * initialized pointing to the correct ColRowInfo
4268 * GnmCell::pos must be valid before this is called. The position is used as the
4272 sheet_cell_add_to_hash (Sheet
*sheet
, GnmCell
*cell
)
4274 g_return_if_fail (cell
->pos
.col
< gnm_sheet_get_max_cols (sheet
));
4275 g_return_if_fail (cell
->pos
.row
< gnm_sheet_get_max_rows (sheet
));
4277 cell
->base
.flags
|= GNM_CELL_IN_SHEET_LIST
;
4279 * fetching the col/row here serve 3 functions
4280 * 1) obsolete: we used to store the pointer in the cell
4281 * 2) Expanding col/row.max_used
4282 * 3) Creating an entry in the COLROW_SEGMENT. Lots and lots of
4283 * things use those to help limit iteration
4285 * For now just call col_fetch even though it is not necessary to
4286 * ensure that 2,3 still happen. Alot will need rewriting to avoid
4287 * these requirements.
4289 (void)sheet_col_fetch (sheet
, cell
->pos
.col
);
4290 (void)sheet_row_fetch (sheet
, cell
->pos
.row
);
4292 gnm_cell_unrender (cell
);
4294 g_hash_table_insert (sheet
->cell_hash
, cell
, cell
);
4296 if (gnm_sheet_merge_is_corner (sheet
, &cell
->pos
))
4297 cell
->base
.flags
|= GNM_CELL_IS_MERGED
;
4300 #undef USE_CELL_POOL
4302 #ifdef USE_CELL_POOL
4303 /* The pool from which all cells are allocated. */
4304 static GOMemChunk
*cell_pool
;
4306 static int cell_allocations
= 0;
4313 #ifdef USE_CELL_POOL
4314 go_mem_chunk_alloc0 (cell_pool
)
4316 (cell_allocations
++, g_slice_new0 (GnmCell
))
4320 cell
->base
.flags
= DEPENDENT_CELL
;
4326 cell_free (GnmCell
*cell
)
4328 g_return_if_fail (cell
!= NULL
);
4330 gnm_cell_cleanout (cell
);
4331 #ifdef USE_CELL_POOL
4332 go_mem_chunk_free (cell_pool
, cell
);
4334 cell_allocations
--, g_slice_free1 (sizeof (*cell
), cell
);
4339 * gnm_sheet_cell_init: (skip)
4342 gnm_sheet_cell_init (void)
4344 #ifdef USE_CELL_POOL
4345 cell_pool
= go_mem_chunk_new ("cell pool",
4351 #ifdef USE_CELL_POOL
4353 cb_cell_pool_leak (gpointer data
, G_GNUC_UNUSED gpointer user
)
4355 GnmCell
const *cell
= data
;
4356 g_printerr ("Leaking cell %p at %s\n", (void *)cell
, cell_name (cell
));
4361 * gnm_sheet_cell_shutdown: (skip)
4364 gnm_sheet_cell_shutdown (void)
4366 #ifdef USE_CELL_POOL
4367 go_mem_chunk_foreach_leak (cell_pool
, cb_cell_pool_leak
, NULL
);
4368 go_mem_chunk_destroy (cell_pool
, FALSE
);
4371 if (cell_allocations
)
4372 g_printerr ("Leaking %d cells.\n", cell_allocations
);
4376 /****************************************************************************/
4379 * sheet_cell_create:
4384 * Creates a new cell and adds it to the sheet hash.
4387 sheet_cell_create (Sheet
*sheet
, int col
, int row
)
4391 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
4392 g_return_val_if_fail (col
>= 0, NULL
);
4393 g_return_val_if_fail (col
< gnm_sheet_get_max_cols (sheet
), NULL
);
4394 g_return_val_if_fail (row
>= 0, NULL
);
4395 g_return_val_if_fail (row
< gnm_sheet_get_max_rows (sheet
), NULL
);
4398 cell
->base
.sheet
= sheet
;
4399 cell
->pos
.col
= col
;
4400 cell
->pos
.row
= row
;
4401 cell
->value
= value_new_empty ();
4403 sheet_cell_add_to_hash (sheet
, cell
);
4408 * sheet_cell_remove_from_hash:
4412 * Removes a cell from the sheet hash, clears any spans, and unlinks it from
4413 * the dependent collection.
4416 sheet_cell_remove_from_hash (Sheet
*sheet
, GnmCell
*cell
)
4418 cell_unregister_span (cell
);
4419 if (gnm_cell_expr_is_linked (cell
))
4420 dependent_unlink (GNM_CELL_TO_DEP (cell
));
4421 g_hash_table_remove (sheet
->cell_hash
, cell
);
4422 cell
->base
.flags
&= ~(GNM_CELL_IN_SHEET_LIST
|GNM_CELL_IS_MERGED
);
4426 * sheet_cell_destroy:
4431 * Remove the cell from the web of dependencies of a
4432 * sheet. Do NOT redraw.
4435 sheet_cell_destroy (Sheet
*sheet
, GnmCell
*cell
, gboolean queue_recalc
)
4437 if (gnm_cell_expr_is_linked (cell
)) {
4438 /* if it needs recalc then its depends are already queued
4439 * check recalc status before we unlink
4441 queue_recalc
&= !gnm_cell_needs_recalc (cell
);
4442 dependent_unlink (GNM_CELL_TO_DEP (cell
));
4446 cell_foreach_dep (cell
, (GnmDepFunc
)dependent_queue_recalc
, NULL
);
4448 sheet_cell_remove_from_hash (sheet
, cell
);
4453 * sheet_cell_remove:
4459 * Remove the cell from the web of dependencies of a
4460 * sheet. Do NOT free the cell, optionally redraw it, optionally
4461 * queue it for recalc.
4464 sheet_cell_remove (Sheet
*sheet
, GnmCell
*cell
,
4465 gboolean redraw
, gboolean queue_recalc
)
4467 g_return_if_fail (cell
!= NULL
);
4468 g_return_if_fail (IS_SHEET (sheet
));
4470 /* Queue a redraw on the region used by the cell being removed */
4472 sheet_redraw_region (sheet
,
4473 cell
->pos
.col
, cell
->pos
.row
,
4474 cell
->pos
.col
, cell
->pos
.row
);
4475 sheet_flag_status_update_cell (cell
);
4478 sheet_cell_destroy (sheet
, cell
, queue_recalc
);
4482 cb_free_cell (GnmCellIter
const *iter
, G_GNUC_UNUSED gpointer user
)
4484 sheet_cell_destroy (iter
->pp
.sheet
, iter
->cell
, FALSE
);
4489 * sheet_col_destroy:
4494 * Destroys a ColRowInfo from the Sheet with all of its cells
4497 sheet_col_destroy (Sheet
*sheet
, int const col
, gboolean free_cells
)
4499 ColRowSegment
**segment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (&(sheet
->cols
), col
);
4500 int const sub
= COLROW_SUB_INDEX (col
);
4501 ColRowInfo
*ci
= NULL
;
4503 if (*segment
== NULL
)
4505 ci
= (*segment
)->info
[sub
];
4509 if (sheet
->cols
.max_outline_level
> 0 &&
4510 sheet
->cols
.max_outline_level
== ci
->outline_level
)
4511 sheet
->priv
->recompute_max_col_group
= TRUE
;
4514 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4515 col
, 0, col
, gnm_sheet_get_last_row (sheet
),
4516 &cb_free_cell
, NULL
);
4518 (*segment
)->info
[sub
] = NULL
;
4521 /* Use >= just in case things are screwed up */
4522 if (col
>= sheet
->cols
.max_used
) {
4524 while (--i
>= 0 && sheet_col_get (sheet
, i
) == NULL
)
4526 sheet
->cols
.max_used
= i
;
4531 * Destroys a row ColRowInfo
4534 sheet_row_destroy (Sheet
*sheet
, int const row
, gboolean free_cells
)
4536 ColRowSegment
**segment
= (ColRowSegment
**)&COLROW_GET_SEGMENT (&(sheet
->rows
), row
);
4537 int const sub
= COLROW_SUB_INDEX (row
);
4538 ColRowInfo
*ri
= NULL
;
4540 if (*segment
== NULL
)
4542 ri
= (*segment
)->info
[sub
];
4546 if (sheet
->rows
.max_outline_level
> 0 &&
4547 sheet
->rows
.max_outline_level
== ri
->outline_level
)
4548 sheet
->priv
->recompute_max_row_group
= TRUE
;
4551 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4552 0, row
, gnm_sheet_get_last_col (sheet
), row
,
4553 &cb_free_cell
, NULL
);
4555 /* Rows have span lists, destroy them too */
4556 row_destroy_span (ri
);
4558 (*segment
)->info
[sub
] = NULL
;
4561 /* Use >= just in case things are screwed up */
4562 if (row
>= sheet
->rows
.max_used
) {
4564 while (--i
>= 0 && sheet_row_get (sheet
, i
) == NULL
)
4566 sheet
->rows
.max_used
= i
;
4571 cb_remove_allcells (G_GNUC_UNUSED gpointer ignore0
, GnmCell
*cell
, G_GNUC_UNUSED gpointer ignore1
)
4573 cell
->base
.flags
&= ~GNM_CELL_IN_SHEET_LIST
;
4578 sheet_destroy_contents (Sheet
*sheet
)
4583 /* By the time we reach here dependencies should have been shut down */
4584 g_return_if_fail (sheet
->deps
== NULL
);
4586 /* A simple test to see if this has already been run. */
4587 if (sheet
->hash_merged
== NULL
)
4591 GSList
*tmp
= sheet
->slicers
;
4592 sheet
->slicers
= NULL
;
4593 g_slist_free_full (tmp
, (GDestroyNotify
)gnm_sheet_slicer_clear_sheet
);
4596 /* These contain SheetObjects, remove them first */
4597 filters
= g_slist_copy (sheet
->filters
);
4598 g_slist_foreach (filters
, (GFunc
)gnm_filter_remove
, NULL
);
4599 g_slist_foreach (filters
, (GFunc
)gnm_filter_unref
, NULL
);
4600 g_slist_free (filters
);
4602 if (sheet
->sheet_objects
) {
4603 /* The list is changed as we remove */
4604 GSList
*objs
= g_slist_copy (sheet
->sheet_objects
);
4606 for (ptr
= objs
; ptr
!= NULL
; ptr
= ptr
->next
) {
4607 SheetObject
*so
= GNM_SO (ptr
->data
);
4609 sheet_object_clear_sheet (so
);
4611 g_slist_free (objs
);
4612 if (sheet
->sheet_objects
!= NULL
)
4613 g_warning ("There is a problem with sheet objects");
4616 /* The memory is managed by Sheet::list_merged */
4617 g_hash_table_destroy (sheet
->hash_merged
);
4618 sheet
->hash_merged
= NULL
;
4620 g_slist_free_full (sheet
->list_merged
, g_free
);
4621 sheet
->list_merged
= NULL
;
4623 /* Clear the row spans 1st */
4624 for (i
= sheet
->rows
.max_used
; i
>= 0 ; --i
)
4625 row_destroy_span (sheet_row_get (sheet
, i
));
4627 /* Remove all the cells */
4628 sheet_cell_foreach (sheet
, (GHFunc
) &cb_remove_allcells
, NULL
);
4629 g_hash_table_destroy (sheet
->cell_hash
);
4631 /* Delete in ascending order to avoid decrementing max_used each time */
4632 for (i
= 0; i
<= sheet
->cols
.max_used
; ++i
)
4633 sheet_col_destroy (sheet
, i
, FALSE
);
4635 for (i
= 0; i
<= sheet
->rows
.max_used
; ++i
)
4636 sheet_row_destroy (sheet
, i
, FALSE
);
4638 /* Free segments too */
4639 col_row_collection_resize (&sheet
->cols
, 0);
4640 g_ptr_array_free (sheet
->cols
.info
, TRUE
);
4641 sheet
->cols
.info
= NULL
;
4643 col_row_collection_resize (&sheet
->rows
, 0);
4644 g_ptr_array_free (sheet
->rows
.info
, TRUE
);
4645 sheet
->rows
.info
= NULL
;
4647 g_clear_object (&sheet
->solver_parameters
);
4652 * @sheet: the sheet to destroy
4654 * Please note that you need to detach this sheet before
4655 * calling this routine or you will get a warning.
4658 sheet_destroy (Sheet
*sheet
)
4660 g_return_if_fail (IS_SHEET (sheet
));
4662 if (sheet
->sheet_views
->len
> 0)
4663 g_warning ("Unexpected left over views");
4665 if (sheet
->print_info
) {
4666 gnm_print_info_free (sheet
->print_info
);
4667 sheet
->print_info
= NULL
;
4670 style_color_unref (sheet
->tab_color
);
4671 sheet
->tab_color
= NULL
;
4672 style_color_unref (sheet
->tab_text_color
);
4673 sheet
->tab_text_color
= NULL
;
4675 gnm_app_clipboard_invalidate_sheet (sheet
);
4679 gnm_sheet_finalize (GObject
*obj
)
4681 Sheet
*sheet
= SHEET (obj
);
4682 gboolean debug_FMR
= gnm_debug_flag ("sheet-fmr");
4684 sheet_destroy (sheet
);
4686 g_clear_object (&sheet
->solver_parameters
);
4688 gnm_conventions_unref (sheet
->convs
);
4689 sheet
->convs
= NULL
;
4691 g_list_free_full (sheet
->scenarios
, g_object_unref
);
4692 sheet
->scenarios
= NULL
;
4694 if (sheet
->sort_setups
!= NULL
)
4695 g_hash_table_unref (sheet
->sort_setups
);
4697 dependents_invalidate_sheet (sheet
, TRUE
);
4699 sheet_destroy_contents (sheet
);
4701 if (sheet
->slicers
!= NULL
) {
4702 g_warning ("DataSlicer list should be NULL");
4704 if (sheet
->filters
!= NULL
) {
4705 g_warning ("Filter list should be NULL");
4707 if (sheet
->sheet_objects
!= NULL
) {
4708 g_warning ("Sheet object list should be NULL");
4710 if (sheet
->list_merged
!= NULL
) {
4711 g_warning ("Merged list should be NULL");
4713 if (sheet
->hash_merged
!= NULL
) {
4714 g_warning ("Merged hash should be NULL");
4717 sheet_style_shutdown (sheet
);
4719 (void) g_idle_remove_by_data (sheet
);
4722 g_printerr ("Sheet %p is %s\n", sheet
, sheet
->name_quoted
);
4724 g_free (sheet
->name_quoted
);
4725 g_free (sheet
->name_unquoted
);
4726 g_free (sheet
->name_unquoted_collate_key
);
4727 g_free (sheet
->name_case_insensitive
);
4729 sheet
->name_quoted
= (char *)0xdeadbeef;
4730 sheet
->name_unquoted
= (char *)0xdeadbeef;
4731 g_free (sheet
->priv
);
4732 g_ptr_array_free (sheet
->sheet_views
, TRUE
);
4734 gnm_rvc_free (sheet
->rendered_values
);
4737 /* Keep object around. */
4741 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
4744 /*****************************************************************************/
4747 * cb_empty_cell: A callback for sheet_foreach_cell_in_range
4748 * removes/clear all of the cells in the specified region.
4749 * Does NOT queue a redraw.
4751 * WARNING : This does NOT regenerate spans that were interrupted by
4752 * this cell and can now continue.
4755 cb_empty_cell (GnmCellIter
const *iter
, gpointer user
)
4757 int clear_flags
= GPOINTER_TO_INT (user
);
4759 /* TODO : here and other places flag a need to update the
4762 if (row
>= sheet
->rows
.max_used
|| col
>= sheet
->cols
.max_used
) { }
4765 sheet_cell_remove (iter
->pp
.sheet
, iter
->cell
, FALSE
,
4766 (clear_flags
& CLEAR_RECALC_DEPS
) &&
4767 iter
->pp
.wb
->recursive_dirty_enabled
);
4773 * sheet_clear_region:
4779 * @clear_flags: If this is TRUE then styles are erased.
4782 * Clears are region of cells
4784 * We assemble a list of cells to destroy, since we will be making changes
4785 * to the structure being manipulated by the sheet_foreach_cell_in_range routine
4788 sheet_clear_region (Sheet
*sheet
,
4789 int start_col
, int start_row
,
4790 int end_col
, int end_row
,
4796 g_return_if_fail (IS_SHEET (sheet
));
4797 g_return_if_fail (start_col
<= end_col
);
4798 g_return_if_fail (start_row
<= end_row
);
4800 r
.start
.col
= start_col
;
4801 r
.start
.row
= start_row
;
4802 r
.end
.col
= end_col
;
4803 r
.end
.row
= end_row
;
4805 if (clear_flags
& CLEAR_VALUES
&& !(clear_flags
& CLEAR_NOCHECKARRAY
) &&
4806 sheet_range_splits_array (sheet
, &r
, NULL
, cc
, _("Clear")))
4809 /* Queue a redraw for cells being modified */
4810 if (clear_flags
& (CLEAR_VALUES
|CLEAR_FORMATS
))
4811 sheet_redraw_region (sheet
,
4812 start_col
, start_row
,
4815 /* Clear the style in the region (new_default will ref the style for us). */
4816 if (clear_flags
& CLEAR_FORMATS
) {
4817 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
4818 sheet_range_calc_spans (sheet
, &r
, GNM_SPANCALC_RE_RENDER
|GNM_SPANCALC_RESIZE
);
4819 rows_height_update (sheet
, &r
, TRUE
);
4822 if (clear_flags
& CLEAR_OBJECTS
)
4823 sheet_objects_clear (sheet
, &r
, G_TYPE_NONE
, NULL
);
4824 else if (clear_flags
& CLEAR_COMMENTS
)
4825 sheet_objects_clear (sheet
, &r
, GNM_CELL_COMMENT_TYPE
, NULL
);
4827 /* TODO : how to handle objects ? */
4828 if (clear_flags
& CLEAR_VALUES
) {
4829 /* Remove or empty the cells depending on
4830 * whether or not there are comments
4832 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
4833 start_col
, start_row
, end_col
, end_row
,
4834 &cb_empty_cell
, GINT_TO_POINTER (clear_flags
));
4836 if (!(clear_flags
& CLEAR_NORESPAN
)) {
4837 sheet_queue_respan (sheet
, start_row
, end_row
);
4838 sheet_flag_status_update_range (sheet
, &r
);
4842 if (clear_flags
& CLEAR_MERGES
) {
4843 GSList
*merged
, *ptr
;
4844 merged
= gnm_sheet_merge_get_overlap (sheet
, &r
);
4845 for (ptr
= merged
; ptr
!= NULL
; ptr
= ptr
->next
)
4846 gnm_sheet_merge_remove (sheet
, ptr
->data
);
4847 g_slist_free (merged
);
4850 if (clear_flags
& CLEAR_RECALC_DEPS
)
4851 sheet_region_queue_recalc (sheet
, &r
);
4854 sheet_redraw_all (sheet
, FALSE
);
4858 sheet_clear_region_cb (GnmSheetRange
*sr
, int *flags
)
4860 sheet_clear_region (sr
->sheet
,
4861 sr
->range
.start
.col
, sr
->range
.start
.row
,
4862 sr
->range
.end
.col
, sr
->range
.end
.row
,
4863 *flags
| CLEAR_NOCHECKARRAY
, NULL
);
4868 * sheet_clear_region_undo:
4869 * @sr: #GnmSheetRange
4870 * @clear_flags: flags.
4872 * Returns: (transfer full): the new #GOUndo.
4874 GOUndo
*sheet_clear_region_undo (GnmSheetRange
*sr
, int clear_flags
)
4876 int *flags
= g_new(int, 1);
4877 *flags
= clear_flags
;
4878 return go_undo_binary_new
4879 (sr
, (gpointer
)flags
,
4880 (GOUndoBinaryFunc
) sheet_clear_region_cb
,
4881 (GFreeFunc
) gnm_sheet_range_free
,
4882 (GFreeFunc
) g_free
);
4886 /*****************************************************************************/
4889 sheet_mark_dirty (Sheet
*sheet
)
4891 g_return_if_fail (IS_SHEET (sheet
));
4893 if (sheet
->workbook
)
4894 go_doc_set_dirty (GO_DOC (sheet
->workbook
), TRUE
);
4897 /****************************************************************************/
4900 sheet_cells_deps_move (GnmExprRelocateInfo
*rinfo
)
4902 Sheet
*sheet
= rinfo
->origin_sheet
;
4903 GPtrArray
*deps
= sheet_cells (sheet
, &rinfo
->origin
);
4906 /* Phase 1: collect all cells and remove them from hash. */
4907 for (ui
= 0; ui
< deps
->len
; ui
++) {
4908 GnmCell
*cell
= g_ptr_array_index (deps
, ui
);
4909 gboolean needs_recalc
= gnm_cell_needs_recalc (cell
);
4910 sheet_cell_remove_from_hash (sheet
, cell
);
4911 if (needs_recalc
) /* Do we need this now? */
4912 cell
->base
.flags
|= DEPENDENT_NEEDS_RECALC
;
4915 /* Phase 2: add all non-cell deps with positions */
4916 SHEET_FOREACH_DEPENDENT
4918 GnmCellPos
const *pos
;
4919 if (!dependent_is_cell (dep
) &&
4920 dependent_has_pos (dep
) &&
4921 (pos
= dependent_pos (dep
)) &&
4922 range_contains (&rinfo
->origin
, pos
->col
, pos
->row
)) {
4923 dependent_unlink (dep
);
4924 g_ptr_array_add (deps
, dep
);
4928 /* Phase 3: move everything and add cells to hash. */
4929 for (ui
= 0; ui
< deps
->len
; ui
++) {
4930 GnmDependent
*dep
= g_ptr_array_index (deps
, ui
);
4932 dependent_move (dep
, rinfo
->col_offset
, rinfo
->row_offset
);
4934 if (dependent_is_cell (dep
))
4935 sheet_cell_add_to_hash (sheet
, GNM_DEP_TO_CELL (dep
));
4938 dependent_link (dep
);
4941 g_ptr_array_free (deps
, TRUE
);
4944 /* Moves the headers to their new location */
4946 sheet_colrow_move (Sheet
*sheet
, gboolean is_cols
,
4947 int const old_pos
, int const new_pos
)
4949 ColRowSegment
*segment
= COLROW_GET_SEGMENT (is_cols
? &sheet
->cols
: &sheet
->rows
, old_pos
);
4950 ColRowInfo
*info
= segment
4951 ? segment
->info
[COLROW_SUB_INDEX (old_pos
)]
4954 g_return_if_fail (old_pos
>= 0);
4955 g_return_if_fail (new_pos
>= 0);
4960 /* Update the position */
4961 segment
->info
[COLROW_SUB_INDEX (old_pos
)] = NULL
;
4962 sheet_colrow_add (sheet
, info
, is_cols
, new_pos
);
4966 sheet_colrow_set_collapse (Sheet
*sheet
, gboolean is_cols
, int pos
)
4969 ColRowInfo
const *vs
= NULL
;
4971 if (pos
< 0 || pos
>= colrow_max (is_cols
, sheet
))
4974 /* grab the next or previous col/row */
4975 if ((is_cols
? sheet
->outline_symbols_right
: sheet
->outline_symbols_below
)) {
4977 vs
= sheet_colrow_get (sheet
, pos
-1, is_cols
);
4978 } else if ((pos
+1) < colrow_max (is_cols
, sheet
))
4979 vs
= sheet_colrow_get (sheet
, pos
+1, is_cols
);
4981 /* handle the case where an empty col/row should be marked collapsed */
4982 cri
= sheet_colrow_get (sheet
, pos
, is_cols
);
4984 cri
->is_collapsed
= (vs
!= NULL
&& !vs
->visible
&&
4985 vs
->outline_level
> cri
->outline_level
);
4986 else if (vs
!= NULL
&& !vs
->visible
&& vs
->outline_level
> 0) {
4987 cri
= sheet_colrow_fetch (sheet
, pos
, is_cols
);
4988 cri
->is_collapsed
= TRUE
;
4993 combine_undo (GOUndo
**pundo
, GOUndo
*u
)
4996 *pundo
= go_undo_combine (*pundo
, u
);
5001 typedef gboolean (*ColRowInsDelFunc
) (Sheet
*sheet
, int idx
, int count
,
5002 GOUndo
**pundo
, GOCmdContext
*cc
);
5005 ColRowInsDelFunc func
;
5010 ColRowStateList
*states
;
5015 cb_undo_insdel (ColRowInsDelData
*data
)
5017 data
->func (data
->sheet
, data
->pos
, data
->count
, NULL
, NULL
);
5018 colrow_set_states (data
->sheet
, data
->is_cols
,
5019 data
->state_start
, data
->states
);
5023 cb_undo_insdel_free (ColRowInsDelData
*data
)
5025 colrow_state_list_destroy (data
->states
);
5030 sheet_insdel_colrow (Sheet
*sheet
, int pos
, int count
,
5031 GOUndo
**pundo
, GOCmdContext
*cc
,
5032 gboolean is_cols
, gboolean is_insert
,
5033 const char *description
,
5034 ColRowInsDelFunc opposite
)
5037 GnmRange kill_zone
; /* The range whose contents will be lost. */
5038 GnmRange move_zone
; /* The range whose contents will be moved. */
5039 GnmRange change_zone
; /* The union of kill_zone and move_zone. */
5040 int i
, last_pos
, max_used_pos
;
5041 int kill_start
, kill_end
, move_start
, move_end
;
5042 int scount
= is_insert
? count
: -count
;
5043 ColRowStateList
*states
= NULL
;
5044 GnmExprRelocateInfo reloc_info
;
5046 gboolean sticky_end
= TRUE
;
5048 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
5049 g_return_val_if_fail (count
> 0, TRUE
);
5052 * The main undo for an insert col/row is delete col/row and vice versa.
5053 * In addition to that, we collect undo information that the main undo
5054 * operation will not restore -- for example the contents of the kill
5057 if (pundo
) *pundo
= NULL
;
5059 last_pos
= colrow_max (is_cols
, sheet
) - 1;
5060 max_used_pos
= is_cols
? sheet
->cols
.max_used
: sheet
->rows
.max_used
;
5062 kill_start
= last_pos
- (count
- 1);
5063 kill_end
= last_pos
;
5065 move_end
= kill_start
- 1;
5067 int max_count
= last_pos
+ 1 - pos
;
5068 if (count
> max_count
) {
5073 kill_end
= pos
+ (count
- 1);
5074 move_start
= kill_end
+ 1;
5075 move_end
= last_pos
;
5077 (is_cols
? range_init_cols
: range_init_rows
)
5078 (&kill_zone
, sheet
, kill_start
, kill_end
);
5079 (is_cols
? range_init_cols
: range_init_rows
)
5080 (&move_zone
, sheet
, move_start
, move_end
);
5081 change_zone
= range_union (&kill_zone
, &move_zone
);
5083 /* 0. Check displaced/deleted region and ensure arrays aren't divided. */
5084 if (sheet_range_splits_array (sheet
, &kill_zone
, NULL
, cc
, description
))
5086 if (move_start
<= move_end
&&
5087 sheet_range_splits_array (sheet
, &move_zone
, NULL
, cc
, description
))
5091 * At this point we're committed. Anything that can go wrong should
5092 * have been ruled out already.
5096 g_printerr ("Action = %s at %d count %d\n", description
, pos
, count
);
5097 g_printerr ("Kill zone: %s\n", range_as_string (&kill_zone
));
5100 /* 1. Delete all columns/rows in the kill zone */
5102 combine_undo (pundo
, clipboard_copy_range_undo (sheet
, &kill_zone
));
5103 states
= colrow_get_states (sheet
, is_cols
, kill_start
, kill_end
);
5105 for (i
= MIN (max_used_pos
, kill_end
); i
>= kill_start
; --i
)
5106 (is_cols
? sheet_col_destroy
: sheet_row_destroy
)
5108 /* Brutally discard auto filter objects. Collect the rest for undo. */
5109 sheet_objects_clear (sheet
, &kill_zone
, GNM_FILTER_COMBO_TYPE
, NULL
);
5110 sheet_objects_clear (sheet
, &kill_zone
, G_TYPE_NONE
, pundo
);
5112 reloc_info
.reloc_type
= is_cols
? GNM_EXPR_RELOCATE_COLS
: GNM_EXPR_RELOCATE_ROWS
;
5113 reloc_info
.sticky_end
= sticky_end
;
5114 reloc_info
.origin_sheet
= reloc_info
.target_sheet
= sheet
;
5115 parse_pos_init_sheet (&reloc_info
.pos
, sheet
);
5117 /* 2. Get rid of style dependents, see #741197. */
5118 sheet_style_clear_style_dependents (sheet
, &change_zone
);
5120 /* 3. Invalidate references to kill zone. */
5122 /* Done in the next step. */
5124 reloc_info
.origin
= kill_zone
;
5125 /* Force invalidation: */
5126 reloc_info
.col_offset
= is_cols
? last_pos
+ 1 : 0;
5127 reloc_info
.row_offset
= is_cols
? 0 : last_pos
+ 1;
5128 combine_undo (pundo
, dependents_relocate (&reloc_info
));
5131 /* 4. Fix references to the cells which are moving */
5132 reloc_info
.origin
= is_insert
? change_zone
: move_zone
;
5133 reloc_info
.col_offset
= is_cols
? scount
: 0;
5134 reloc_info
.row_offset
= is_cols
? 0 : scount
;
5135 combine_undo (pundo
, dependents_relocate (&reloc_info
));
5137 /* 5. Move the cells */
5138 sheet_cells_deps_move (&reloc_info
);
5140 /* 6. Move the columns/rows to their new location. */
5142 /* From right to left */
5143 for (i
= max_used_pos
; i
>= pos
; --i
)
5144 sheet_colrow_move (sheet
, is_cols
, i
, i
+ count
);
5146 /* From left to right */
5147 for (i
= pos
+ count
; i
<= max_used_pos
; ++i
)
5148 sheet_colrow_move (sheet
, is_cols
, i
, i
- count
);
5150 sheet_colrow_set_collapse (sheet
, is_cols
, pos
);
5151 sheet_colrow_set_collapse (sheet
, is_cols
,
5152 is_insert
? pos
+ count
: last_pos
- (count
- 1));
5154 /* 7. Move formatting. */
5155 sheet_style_insdel_colrow (&reloc_info
);
5157 /* 8. Move objects. */
5158 sheet_objects_relocate (&reloc_info
, FALSE
, pundo
);
5160 /* 9. Move merges. */
5161 gnm_sheet_merge_relocate (&reloc_info
, pundo
);
5163 /* 10. Move filters. */
5164 gnm_sheet_filter_insdel_colrow (sheet
, is_cols
, is_insert
, pos
, count
, pundo
);
5166 /* Notify sheet of pending updates */
5167 sheet_mark_dirty (sheet
);
5168 sheet
->priv
->recompute_visibility
= TRUE
;
5169 sheet_flag_recompute_spans (sheet
);
5170 sheet_flag_status_update_range (sheet
, &change_zone
);
5172 sheet
->priv
->reposition_objects
.col
= pos
;
5174 sheet
->priv
->reposition_objects
.row
= pos
;
5176 /* WARNING WARNING WARNING
5177 * This is bad practice and should not really be here.
5178 * However, we need to ensure that update is run before
5179 * sv_panes_insdel_colrow plays with frozen panes, updating those can
5180 * trigger redraws before sheet_update has been called. */
5181 sheet_update (sheet
);
5183 SHEET_FOREACH_VIEW (sheet
, sv
,
5184 sv_panes_insdel_colrow (sv
, is_cols
, is_insert
, pos
, count
););
5186 /* The main undo is the opposite operation. */
5188 ColRowInsDelData
*data
;
5191 data
= g_new (ColRowInsDelData
, 1);
5192 data
->func
= opposite
;
5193 data
->sheet
= sheet
;
5194 data
->is_cols
= is_cols
;
5196 data
->count
= count
;
5197 data
->states
= states
;
5198 data
->state_start
= kill_start
;
5200 u
= go_undo_unary_new (data
, (GOUndoUnaryFunc
)cb_undo_insdel
,
5201 (GFreeFunc
)cb_undo_insdel_free
);
5203 combine_undo (pundo
, u
);
5206 /* Reapply all filters. */
5207 for (l
= sheet
->filters
; l
; l
= l
->next
) {
5208 GnmFilter
*filter
= l
->data
;
5209 gnm_filter_reapply (filter
);
5216 * sheet_insert_cols:
5218 * @col: At which position we want to insert
5219 * @count: The number of columns to be inserted
5220 * @pundo: (out): (transfer full): (allow-none): undo closure
5224 sheet_insert_cols (Sheet
*sheet
, int col
, int count
,
5225 GOUndo
**pundo
, GOCmdContext
*cc
)
5227 return sheet_insdel_colrow (sheet
, col
, count
, pundo
, cc
,
5229 _("Insert Columns"),
5234 * sheet_delete_cols:
5236 * @col: At which position we want to start deleting columns
5237 * @count: The number of columns to be deleted
5238 * @pundo: (out): (transfer full): (allow-none): undo closure
5239 * @cc: The command context
5242 sheet_delete_cols (Sheet
*sheet
, int col
, int count
,
5243 GOUndo
**pundo
, GOCmdContext
*cc
)
5245 return sheet_insdel_colrow (sheet
, col
, count
, pundo
, cc
,
5247 _("Delete Columns"),
5252 * sheet_insert_rows:
5254 * @row: At which position we want to insert
5255 * @count: The number of rows to be inserted
5256 * @pundo: (out): (transfer full): (allow-none): undo closure
5257 * @cc: The command context
5260 sheet_insert_rows (Sheet
*sheet
, int row
, int count
,
5261 GOUndo
**pundo
, GOCmdContext
*cc
)
5263 return sheet_insdel_colrow (sheet
, row
, count
, pundo
, cc
,
5270 * sheet_delete_rows:
5272 * @row: At which position we want to start deleting rows
5273 * @count: The number of rows to be deleted
5274 * @pundo: (out): (transfer full): (allow-none): undo closure
5275 * @cc: The command context
5278 sheet_delete_rows (Sheet
*sheet
, int row
, int count
,
5279 GOUndo
**pundo
, GOCmdContext
*cc
)
5281 return sheet_insdel_colrow (sheet
, row
, count
, pundo
, cc
,
5288 * Callback for sheet_foreach_cell_in_range to remove a cell from the sheet
5289 * hash, unlink from the dependent collection and put it in a temporary list.
5292 cb_collect_cell (GnmCellIter
const *iter
, gpointer user
)
5295 GnmCell
*cell
= iter
->cell
;
5296 gboolean needs_recalc
= gnm_cell_needs_recalc (cell
);
5298 sheet_cell_remove_from_hash (iter
->pp
.sheet
, cell
);
5299 *l
= g_list_prepend (*l
, cell
);
5301 cell
->base
.flags
|= DEPENDENT_NEEDS_RECALC
;
5309 * @pundo: optionally NULL, caller releases result
5311 * Move a range as specified in @rinfo report warnings to @cc.
5312 * if @pundo is non NULL, invalidate references to the
5313 * target region that are being cleared, and store the undo information
5314 * in @pundo. If it is NULL do NOT INVALIDATE.
5317 sheet_move_range (GnmExprRelocateInfo
const *rinfo
,
5318 GOUndo
**pundo
, GOCmdContext
*cc
)
5320 GList
*cells
= NULL
;
5323 gboolean out_of_range
;
5325 g_return_if_fail (rinfo
!= NULL
);
5326 g_return_if_fail (IS_SHEET (rinfo
->origin_sheet
));
5327 g_return_if_fail (IS_SHEET (rinfo
->target_sheet
));
5328 g_return_if_fail (rinfo
->origin_sheet
!= rinfo
->target_sheet
||
5329 rinfo
->col_offset
!= 0 ||
5330 rinfo
->row_offset
!= 0);
5332 dst
= rinfo
->origin
;
5333 out_of_range
= range_translate (&dst
, rinfo
->target_sheet
,
5334 rinfo
->col_offset
, rinfo
->row_offset
);
5336 /* Redraw the src region in case anything was spanning */
5337 sheet_redraw_range (rinfo
->origin_sheet
, &rinfo
->origin
);
5339 /* 1. invalidate references to any cells in the destination range that
5340 * are not shared with the src. This must be done before the references
5341 * to from the src range are adjusted because they will point into
5344 if (pundo
!= NULL
) {
5346 if (!out_of_range
) {
5348 GnmExprRelocateInfo reloc_info
;
5350 /* We need to be careful about invalidating references
5351 * to the old content of the destination region. We
5352 * only invalidate references to regions that are
5353 * actually lost. However, this care is only necessary
5354 * if the source and target sheets are the same.
5356 * Handle dst cells being pasted over
5358 if (rinfo
->origin_sheet
== rinfo
->target_sheet
&&
5359 range_overlap (&rinfo
->origin
, &dst
))
5360 invalid
= range_split_ranges (&rinfo
->origin
, &dst
);
5362 invalid
= g_slist_append (NULL
, gnm_range_dup (&dst
));
5364 reloc_info
.origin_sheet
= reloc_info
.target_sheet
= rinfo
->target_sheet
;
5366 /* send to infinity to invalidate, but try to assist
5367 * the relocation heuristics only move in 1
5368 * dimension if possible to give us a chance to be
5369 * smart about partial invalidations */
5370 reloc_info
.col_offset
= gnm_sheet_get_max_cols (rinfo
->target_sheet
);
5371 reloc_info
.row_offset
= gnm_sheet_get_max_rows (rinfo
->target_sheet
);
5372 reloc_info
.sticky_end
= TRUE
;
5373 if (rinfo
->col_offset
== 0) {
5374 reloc_info
.col_offset
= 0;
5375 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_ROWS
;
5376 } else if (rinfo
->row_offset
== 0) {
5377 reloc_info
.row_offset
= 0;
5378 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_COLS
;
5380 reloc_info
.reloc_type
= GNM_EXPR_RELOCATE_MOVE_RANGE
;
5382 parse_pos_init_sheet (&reloc_info
.pos
,
5383 rinfo
->origin_sheet
);
5386 GnmRange
*r
= invalid
->data
;
5387 invalid
= g_slist_remove (invalid
, r
);
5388 if (!range_overlap (r
, &rinfo
->origin
)) {
5389 reloc_info
.origin
= *r
;
5390 combine_undo (pundo
,
5391 dependents_relocate (&reloc_info
));
5397 * DO NOT handle src cells moving out the bounds.
5398 * that is handled elsewhere.
5402 /* 2. Fix references to and from the cells which are moving */
5403 combine_undo (pundo
, dependents_relocate (rinfo
));
5406 /* 3. Collect the cells */
5407 sheet_foreach_cell_in_range (rinfo
->origin_sheet
, CELL_ITER_IGNORE_NONEXISTENT
,
5408 rinfo
->origin
.start
.col
, rinfo
->origin
.start
.row
,
5409 rinfo
->origin
.end
.col
, rinfo
->origin
.end
.row
,
5410 &cb_collect_cell
, &cells
);
5412 /* Reverse list so that we start at the top left (simplifies arrays). */
5413 cells
= g_list_reverse (cells
);
5415 /* 4. Clear the target area & invalidate references to it */
5417 /* we can clear content but not styles from the destination
5418 * region without worrying if it overlaps with the source,
5419 * because we have already extracted the content. However,
5420 * we do need to queue anything that depends on the region for
5422 sheet_clear_region (rinfo
->target_sheet
,
5423 dst
.start
.col
, dst
.start
.row
,
5424 dst
.end
.col
, dst
.end
.row
,
5425 CLEAR_VALUES
|CLEAR_RECALC_DEPS
, cc
);
5427 /* 5. Slide styles BEFORE the cells so that spans get computed properly */
5428 sheet_style_relocate (rinfo
);
5430 /* 6. Insert the cells back */
5431 for (; cells
!= NULL
; cells
= g_list_remove (cells
, cell
)) {
5434 /* check for out of bounds and delete if necessary */
5435 if ((cell
->pos
.col
+ rinfo
->col_offset
) >= gnm_sheet_get_max_cols (rinfo
->target_sheet
) ||
5436 (cell
->pos
.row
+ rinfo
->row_offset
) >= gnm_sheet_get_max_rows (rinfo
->target_sheet
)) {
5441 /* Update the location */
5442 cell
->base
.sheet
= rinfo
->target_sheet
;
5443 cell
->pos
.col
+= rinfo
->col_offset
;
5444 cell
->pos
.row
+= rinfo
->row_offset
;
5445 sheet_cell_add_to_hash (rinfo
->target_sheet
, cell
);
5446 if (gnm_cell_has_expr (cell
))
5447 dependent_link (GNM_CELL_TO_DEP (cell
));
5450 /* 7. Move objects in the range */
5451 sheet_objects_relocate (rinfo
, TRUE
, pundo
);
5452 gnm_sheet_merge_relocate (rinfo
, pundo
);
5454 /* 8. Notify sheet of pending update */
5455 sheet_flag_recompute_spans (rinfo
->origin_sheet
);
5456 sheet_flag_status_update_range (rinfo
->origin_sheet
, &rinfo
->origin
);
5460 sheet_colrow_default_calc (Sheet
*sheet
, double units
,
5461 gboolean is_cols
, gboolean is_pts
)
5463 ColRowInfo
*cri
= is_cols
5464 ? &sheet
->cols
.default_style
5465 : &sheet
->rows
.default_style
;
5467 g_return_if_fail (units
> 0.);
5469 cri
->is_default
= TRUE
;
5470 cri
->hard_size
= FALSE
;
5471 cri
->visible
= TRUE
;
5475 cri
->size_pts
= units
;
5476 colrow_compute_pixels_from_pts (cri
, sheet
, is_cols
, -1);
5478 cri
->size_pixels
= units
;
5479 colrow_compute_pts_from_pixels (cri
, sheet
, is_cols
, -1);
5483 /************************************************************************/
5484 /* Col width support routines.
5488 * sheet_col_get_distance_pts:
5490 * Return the number of points between from_col to to_col
5491 * measured from the upper left corner.
5494 sheet_col_get_distance_pts (Sheet
const *sheet
, int from
, int to
)
5496 ColRowInfo
const *ci
;
5497 double dflt
, pts
= 0., sign
= 1.;
5500 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5509 g_return_val_if_fail (from
>= 0, 1.);
5510 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1.);
5512 /* Do not use col_row_collection_foreach, it ignores empties */
5513 dflt
= sheet
->cols
.default_style
.size_pts
;
5514 for (i
= from
; i
< to
; ++i
) {
5515 if (NULL
== (ci
= sheet_col_get (sheet
, i
)))
5517 else if (ci
->visible
)
5518 pts
+= ci
->size_pts
;
5521 if (sheet
->display_formulas
)
5528 * sheet_col_get_distance_pixels:
5530 * Return the number of pixels between from_col to to_col
5531 * measured from the upper left corner.
5534 sheet_col_get_distance_pixels (Sheet
const *sheet
, int from
, int to
)
5536 ColRowInfo
const *ci
;
5537 int dflt
, pixels
= 0, sign
= 1;
5540 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5549 g_return_val_if_fail (from
>= 0, 1);
5550 g_return_val_if_fail (to
<= gnm_sheet_get_max_cols (sheet
), 1);
5552 /* Do not use col_row_collection_foreach, it ignores empties */
5553 dflt
= sheet_col_get_default_size_pixels (sheet
);
5554 for (i
= from
; i
< to
; ++i
) {
5555 if (NULL
== (ci
= sheet_col_get (sheet
, i
)))
5557 else if (ci
->visible
)
5558 pixels
+= ci
->size_pixels
;
5561 return pixels
* sign
;
5565 * sheet_col_set_size_pts:
5568 * @width_pts: The desired widtht in pts
5569 * @set_by_user: TRUE if this was done by a user (ie, user manually
5572 * Sets width of a col in pts, INCLUDING left and right margins, and the far
5573 * grid line. This is a low level internal routine. It does NOT redraw,
5574 * or reposition objects.
5577 sheet_col_set_size_pts (Sheet
*sheet
, int col
, double width_pts
,
5578 gboolean set_by_user
)
5582 g_return_if_fail (IS_SHEET (sheet
));
5583 g_return_if_fail (width_pts
> 0.0);
5585 ci
= sheet_col_fetch (sheet
, col
);
5586 ci
->hard_size
= set_by_user
;
5587 if (ci
->size_pts
== width_pts
)
5590 ci
->size_pts
= width_pts
;
5591 colrow_compute_pixels_from_pts (ci
, sheet
, TRUE
, -1);
5593 sheet
->priv
->recompute_visibility
= TRUE
;
5594 sheet_flag_recompute_spans (sheet
);
5595 if (sheet
->priv
->reposition_objects
.col
> col
)
5596 sheet
->priv
->reposition_objects
.col
= col
;
5600 sheet_col_set_size_pixels (Sheet
*sheet
, int col
, int width_pixels
,
5601 gboolean set_by_user
)
5605 g_return_if_fail (IS_SHEET (sheet
));
5606 g_return_if_fail (width_pixels
> 0.0);
5608 ci
= sheet_col_fetch (sheet
, col
);
5609 ci
->hard_size
= set_by_user
;
5610 if (ci
->size_pixels
== width_pixels
)
5613 ci
->size_pixels
= width_pixels
;
5614 colrow_compute_pts_from_pixels (ci
, sheet
, TRUE
, -1);
5616 sheet
->priv
->recompute_visibility
= TRUE
;
5617 sheet_flag_recompute_spans (sheet
);
5618 if (sheet
->priv
->reposition_objects
.col
> col
)
5619 sheet
->priv
->reposition_objects
.col
= col
;
5623 * sheet_col_get_default_size_pts:
5625 * Return the default number of pts in a column, including margins.
5626 * This function returns the raw sum, no rounding etc.
5629 sheet_col_get_default_size_pts (Sheet
const *sheet
)
5631 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5632 return sheet
->cols
.default_style
.size_pts
;
5636 sheet_col_get_default_size_pixels (Sheet
const *sheet
)
5638 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5639 return sheet
->cols
.default_style
.size_pixels
;
5643 sheet_col_set_default_size_pts (Sheet
*sheet
, double width_pts
)
5645 g_return_if_fail (IS_SHEET (sheet
));
5646 g_return_if_fail (width_pts
> 0.);
5648 sheet_colrow_default_calc (sheet
, width_pts
, TRUE
, TRUE
);
5649 sheet
->priv
->recompute_visibility
= TRUE
;
5650 sheet_flag_recompute_spans (sheet
);
5651 sheet
->priv
->reposition_objects
.col
= 0;
5654 sheet_col_set_default_size_pixels (Sheet
*sheet
, int width_pixels
)
5656 g_return_if_fail (IS_SHEET (sheet
));
5658 sheet_colrow_default_calc (sheet
, width_pixels
, TRUE
, FALSE
);
5659 sheet
->priv
->recompute_visibility
= TRUE
;
5660 sheet_flag_recompute_spans (sheet
);
5661 sheet
->priv
->reposition_objects
.col
= 0;
5664 /**************************************************************************/
5665 /* Row height support routines
5669 * sheet_row_get_distance_pts:
5671 * Return the number of points between from_row to to_row
5672 * measured from the upper left corner.
5675 sheet_row_get_distance_pts (Sheet
const *sheet
, int from
, int to
)
5677 ColRowSegment
const *segment
;
5678 ColRowInfo
const *ri
;
5679 double const default_size
= sheet
->rows
.default_style
.size_pts
;
5680 double pts
= 0., sign
= 1.;
5683 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5692 g_return_val_if_fail (from
>= 0, 1.);
5693 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1.);
5695 /* Do not use col_row_collection_foreach, it ignores empties.
5696 * Optimize this so that long jumps are not quite so horrific
5699 for (i
= from
; i
< to
; ++i
) {
5700 segment
= COLROW_GET_SEGMENT (&(sheet
->rows
), i
);
5702 if (segment
!= NULL
) {
5703 ri
= segment
->info
[COLROW_SUB_INDEX (i
)];
5705 pts
+= default_size
;
5706 else if (ri
->visible
)
5707 pts
+= ri
->size_pts
;
5709 int segment_end
= COLROW_SEGMENT_END (i
)+1;
5710 if (segment_end
> to
)
5712 pts
+= default_size
* (segment_end
- i
);
5721 * sheet_row_get_distance_pixels:
5723 * Return the number of pixels between from_row to to_row
5724 * measured from the upper left corner.
5727 sheet_row_get_distance_pixels (Sheet
const *sheet
, int from
, int to
)
5729 ColRowInfo
const *ci
;
5730 int dflt
, pixels
= 0, sign
= 1;
5733 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5742 g_return_val_if_fail (from
>= 0, 1);
5743 g_return_val_if_fail (to
<= gnm_sheet_get_max_rows (sheet
), 1);
5745 /* Do not use col_row_collection_foreach, it ignores empties */
5746 dflt
= sheet_row_get_default_size_pixels (sheet
);
5747 for (i
= from
; i
< to
; ++i
) {
5748 if (NULL
== (ci
= sheet_row_get (sheet
, i
)))
5750 else if (ci
->visible
)
5751 pixels
+= ci
->size_pixels
;
5754 return pixels
* sign
;
5758 * sheet_row_set_size_pts:
5761 * @height_pts: The desired height in pts
5762 * @set_by_user: TRUE if this was done by a user (ie, user manually
5765 * Sets height of a row in pts, INCLUDING top and bottom margins, and the lower
5766 * grid line. This is a low level internal routine. It does NOT redraw,
5767 * or reposition objects.
5770 sheet_row_set_size_pts (Sheet
*sheet
, int row
, double height_pts
,
5771 gboolean set_by_user
)
5775 g_return_if_fail (IS_SHEET (sheet
));
5776 g_return_if_fail (height_pts
> 0.0);
5778 ri
= sheet_row_fetch (sheet
, row
);
5779 ri
->hard_size
= set_by_user
;
5780 if (ri
->size_pts
== height_pts
)
5783 ri
->size_pts
= height_pts
;
5784 colrow_compute_pixels_from_pts (ri
, sheet
, FALSE
, -1);
5786 sheet
->priv
->recompute_visibility
= TRUE
;
5787 if (sheet
->priv
->reposition_objects
.row
> row
)
5788 sheet
->priv
->reposition_objects
.row
= row
;
5792 * sheet_row_set_size_pixels:
5795 * @height_pixels: The desired height
5796 * @set_by_user: TRUE if this was done by a user (ie, user manually
5799 * Sets height of a row in pixels, INCLUDING top and bottom margins, and the lower
5803 sheet_row_set_size_pixels (Sheet
*sheet
, int row
, int height_pixels
,
5804 gboolean set_by_user
)
5808 g_return_if_fail (IS_SHEET (sheet
));
5809 g_return_if_fail (height_pixels
> 0);
5811 ri
= sheet_row_fetch (sheet
, row
);
5812 ri
->hard_size
= set_by_user
;
5813 if (ri
->size_pixels
== height_pixels
)
5816 ri
->size_pixels
= height_pixels
;
5817 colrow_compute_pts_from_pixels (ri
, sheet
, FALSE
, -1);
5819 sheet
->priv
->recompute_visibility
= TRUE
;
5820 if (sheet
->priv
->reposition_objects
.row
> row
)
5821 sheet
->priv
->reposition_objects
.row
= row
;
5825 * sheet_row_get_default_size_pts:
5827 * Return the defaul number of units in a row, including margins.
5828 * This function returns the raw sum, no rounding etc.
5831 sheet_row_get_default_size_pts (Sheet
const *sheet
)
5833 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5834 return sheet
->rows
.default_style
.size_pts
;
5838 sheet_row_get_default_size_pixels (Sheet
const *sheet
)
5840 g_return_val_if_fail (IS_SHEET (sheet
), 1.);
5841 return sheet
->rows
.default_style
.size_pixels
;
5845 sheet_row_set_default_size_pts (Sheet
*sheet
, double height_pts
)
5847 g_return_if_fail (IS_SHEET (sheet
));
5849 sheet_colrow_default_calc (sheet
, height_pts
, FALSE
, TRUE
);
5850 sheet
->priv
->recompute_visibility
= TRUE
;
5851 sheet
->priv
->reposition_objects
.row
= 0;
5855 sheet_row_set_default_size_pixels (Sheet
*sheet
, int height_pixels
)
5857 g_return_if_fail (IS_SHEET (sheet
));
5859 sheet_colrow_default_calc (sheet
, height_pixels
, FALSE
, FALSE
);
5860 sheet
->priv
->recompute_visibility
= TRUE
;
5861 sheet
->priv
->reposition_objects
.row
= 0;
5864 /****************************************************************************/
5867 sheet_scrollbar_config (Sheet
const *sheet
)
5869 g_return_if_fail (IS_SHEET (sheet
));
5871 SHEET_FOREACH_CONTROL (sheet
, view
, control
,
5872 sc_scrollbar_config (control
););
5875 /*****************************************************************************/
5880 } closure_clone_colrow
;
5883 sheet_clone_colrow_info_item (GnmColRowIter
const *iter
, void *user_data
)
5885 closure_clone_colrow
const *closure
= user_data
;
5886 ColRowInfo
*new_colrow
= sheet_colrow_fetch (closure
->sheet
,
5887 iter
->pos
, closure
->is_column
);
5888 col_row_info_copy (new_colrow
, iter
->cri
);
5893 sheet_dup_colrows (Sheet
const *src
, Sheet
*dst
)
5895 closure_clone_colrow closure
;
5896 int max_col
= MIN (gnm_sheet_get_max_cols (src
), gnm_sheet_get_max_cols (dst
)),
5897 max_row
= MIN (gnm_sheet_get_max_rows (src
), gnm_sheet_get_max_rows (dst
));
5899 closure
.sheet
= dst
;
5900 closure
.is_column
= TRUE
;
5901 col_row_collection_foreach (&src
->cols
, 0, max_col
- 1,
5902 &sheet_clone_colrow_info_item
, &closure
);
5903 closure
.is_column
= FALSE
;
5904 col_row_collection_foreach (&src
->rows
, 0, max_row
- 1,
5905 &sheet_clone_colrow_info_item
, &closure
);
5907 sheet_col_set_default_size_pixels (dst
,
5908 sheet_col_get_default_size_pixels (src
));
5909 sheet_row_set_default_size_pixels (dst
,
5910 sheet_row_get_default_size_pixels (src
));
5912 dst
->cols
.max_outline_level
= src
->cols
.max_outline_level
;
5913 dst
->rows
.max_outline_level
= src
->rows
.max_outline_level
;
5917 sheet_dup_styles (Sheet
const *src
, Sheet
*dst
)
5919 static GnmCellPos
const corner
= { 0, 0 };
5921 GnmStyleList
*styles
;
5923 sheet_style_set_auto_pattern_color (
5924 dst
, sheet_style_get_auto_pattern_color (src
));
5926 styles
= sheet_style_get_range (src
, range_init_full_sheet (&r
, src
));
5927 sheet_style_set_list (dst
, &corner
, styles
, NULL
, NULL
);
5928 style_list_free (styles
);
5932 sheet_dup_merged_regions (Sheet
const *src
, Sheet
*dst
)
5936 for (ptr
= src
->list_merged
; ptr
!= NULL
; ptr
= ptr
->next
)
5937 gnm_sheet_merge_add (dst
, ptr
->data
, FALSE
, NULL
);
5941 sheet_dup_names (Sheet
const *src
, Sheet
*dst
)
5943 GSList
*names
= gnm_named_expr_collection_list (src
->names
);
5950 parse_pos_init_sheet (&dst_pp
, dst
);
5952 /* Pass 1: add placeholders. */
5953 for (l
= names
; l
; l
= l
->next
) {
5954 GnmNamedExpr
*src_nexpr
= l
->data
;
5955 char const *name
= expr_name_name (src_nexpr
);
5956 GnmNamedExpr
*dst_nexpr
=
5957 gnm_named_expr_collection_lookup (dst
->names
, name
);
5958 GnmExprTop
const *texpr
;
5963 texpr
= gnm_expr_top_new_constant (value_new_empty ());
5964 expr_name_add (&dst_pp
, name
, texpr
, NULL
, TRUE
, NULL
);
5967 /* Pass 2: assign the right expression. */
5968 for (l
= names
; l
; l
= l
->next
) {
5969 GnmNamedExpr
*src_nexpr
= l
->data
;
5970 char const *name
= expr_name_name (src_nexpr
);
5971 GnmNamedExpr
*dst_nexpr
=
5972 gnm_named_expr_collection_lookup (dst
->names
, name
);
5973 GnmExprTop
const *texpr
;
5976 g_warning ("Trouble while duplicating name %s", name
);
5980 if (!dst_nexpr
->is_editable
)
5983 texpr
= gnm_expr_top_relocate_sheet (src_nexpr
->texpr
, src
, dst
);
5984 expr_name_set_expr (dst_nexpr
, texpr
);
5987 g_slist_free (names
);
5991 cb_sheet_cell_copy (G_GNUC_UNUSED gpointer unused
, gpointer key
, gpointer new_sheet_param
)
5993 GnmCell
const *cell
= key
;
5994 Sheet
*dst
= new_sheet_param
;
5996 GnmExprTop
const *texpr
;
5998 g_return_if_fail (dst
!= NULL
);
5999 g_return_if_fail (cell
!= NULL
);
6001 src
= cell
->base
.sheet
;
6002 texpr
= cell
->base
.texpr
;
6004 if (texpr
&& gnm_expr_top_is_array_corner (texpr
)) {
6007 texpr
= gnm_expr_top_relocate_sheet (texpr
, src
, dst
);
6008 gnm_expr_top_get_array_size (texpr
, &cols
, &rows
);
6010 gnm_cell_set_array_formula (dst
,
6011 cell
->pos
.col
, cell
->pos
.row
,
6012 cell
->pos
.col
+ cols
- 1,
6013 cell
->pos
.row
+ rows
- 1,
6014 gnm_expr_top_new (gnm_expr_copy (gnm_expr_top_get_array_expr (texpr
))));
6016 gnm_expr_top_unref (texpr
);
6017 } else if (texpr
&& gnm_expr_top_is_array_elem (texpr
, NULL
, NULL
)) {
6018 /* Not a corner -- ignore. */
6020 GnmCell
*new_cell
= sheet_cell_create (dst
, cell
->pos
.col
, cell
->pos
.row
);
6021 if (gnm_cell_has_expr (cell
)) {
6022 texpr
= gnm_expr_top_relocate_sheet (texpr
, src
, dst
);
6023 gnm_cell_set_expr_and_value (new_cell
, texpr
, value_new_empty (), TRUE
);
6024 gnm_expr_top_unref (texpr
);
6026 gnm_cell_set_value (new_cell
, value_dup (cell
->value
));
6031 sheet_dup_cells (Sheet
const *src
, Sheet
*dst
)
6033 sheet_cell_foreach (src
, &cb_sheet_cell_copy
, dst
);
6034 sheet_region_queue_recalc (dst
, NULL
);
6038 sheet_dup_filters (Sheet
const *src
, Sheet
*dst
)
6041 for (ptr
= src
->filters
; ptr
!= NULL
; ptr
= ptr
->next
)
6042 gnm_filter_dup (ptr
->data
, dst
);
6043 dst
->filters
= g_slist_reverse (dst
->filters
);
6048 * @source_sheet: #Sheet
6050 * Create a new Sheet and return it.
6051 * Returns: (transfer full): the newly allocated #Sheet.
6054 sheet_dup (Sheet
const *src
)
6061 g_return_val_if_fail (IS_SHEET (src
), NULL
);
6062 g_return_val_if_fail (src
->workbook
!= NULL
, NULL
);
6065 name
= workbook_sheet_get_free_name (wb
, src
->name_unquoted
,
6067 dst
= sheet_new_with_type (wb
, name
, src
->sheet_type
,
6068 src
->size
.max_cols
, src
->size
.max_rows
);
6071 dst
->protected_allow
= src
->protected_allow
;
6073 "zoom-factor", src
->last_zoom_factor_used
,
6074 "text-is-rtl", src
->text_is_rtl
,
6075 "visibility", src
->visibility
,
6076 "protected", src
->is_protected
,
6077 "display-formulas", src
->display_formulas
,
6078 "display-zeros", !src
->hide_zero
,
6079 "display-grid", !src
->hide_grid
,
6080 "display-column-header", !src
->hide_col_header
,
6081 "display-row-header", !src
->hide_row_header
,
6082 "display-outlines", src
->display_outlines
,
6083 "display-outlines-below", src
->outline_symbols_below
,
6084 "display-outlines-right", src
->outline_symbols_right
,
6085 "conventions", src
->convs
,
6086 "tab-foreground", src
->tab_text_color
,
6087 "tab-background", src
->tab_color
,
6090 gnm_print_info_free (dst
->print_info
);
6091 dst
->print_info
= gnm_print_info_dup (src
->print_info
);
6093 sheet_dup_styles (src
, dst
);
6094 sheet_dup_merged_regions (src
, dst
);
6095 sheet_dup_colrows (src
, dst
);
6096 sheet_dup_names (src
, dst
);
6097 sheet_dup_cells (src
, dst
);
6098 sheet_objects_dup (src
, dst
, NULL
);
6099 sheet_dup_filters (src
, dst
); /* must be after objects */
6101 #warning selection is in view
6102 #warning freeze/thaw is in view
6104 g_object_unref (dst
->solver_parameters
);
6105 dst
->solver_parameters
= gnm_solver_param_dup (src
->solver_parameters
, dst
);
6107 for (l
= src
->scenarios
; l
; l
= l
->next
) {
6108 GnmScenario
*src_sc
= l
->data
;
6109 GnmScenario
*dst_sc
= gnm_scenario_dup (src_sc
, dst
);
6110 dst
->scenarios
= g_list_prepend (dst
->scenarios
, dst_sc
);
6112 dst
->scenarios
= g_list_reverse (dst
->scenarios
);
6114 sheet_mark_dirty (dst
);
6115 sheet_redraw_all (dst
, TRUE
);
6121 * sheet_set_outline_direction:
6123 * @is_cols: use cols or rows
6125 * When changing the placement of outline collapse markers the flags
6126 * need to be recomputed.
6129 sheet_set_outline_direction (Sheet
*sheet
, gboolean is_cols
)
6132 g_return_if_fail (IS_SHEET (sheet
));
6134 /* not particularly efficient, but this is not a hot spot */
6135 for (i
= colrow_max (is_cols
, sheet
); i
-- > 0 ; )
6136 sheet_colrow_set_collapse (sheet
, is_cols
, i
);
6144 * Find the SheetView corresponding to the supplied @wbv.
6145 * Returns: (transfer none): the view.
6148 sheet_get_view (Sheet
const *sheet
, WorkbookView
const *wbv
)
6153 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6155 SHEET_FOREACH_VIEW (sheet
, view
, {
6156 if (sv_wbv (view
) == wbv
)
6163 cb_queue_respan (GnmColRowIter
const *iter
, void *user_data
)
6165 ((ColRowInfo
*)(iter
->cri
))->needs_respan
= TRUE
;
6170 * sheet_queue_respan:
6175 * queues a span generation for the selected rows.
6176 * the caller is responsible for queuing a redraw
6179 sheet_queue_respan (Sheet
const *sheet
, int start_row
, int end_row
)
6181 col_row_collection_foreach (&sheet
->rows
, start_row
, end_row
,
6182 cb_queue_respan
, NULL
);
6186 sheet_cell_queue_respan (GnmCell
*cell
)
6188 ColRowInfo
*ri
= sheet_row_get (cell
->base
.sheet
, cell
->pos
.row
);
6189 ri
->needs_respan
= TRUE
;
6194 * sheet_get_comment:
6195 * @sheet: #Sheet const *
6196 * @pos: #GnmCellPos const *
6198 * If there is a cell comment at @pos in @sheet return it.
6200 * Caller does get a reference to the object if it exists.
6201 * Returns: (transfer full): the comment or %NULL.
6204 sheet_get_comment (Sheet
const *sheet
, GnmCellPos
const *pos
)
6212 mr
= gnm_sheet_merge_contains_pos (sheet
, pos
);
6215 comments
= sheet_objects_get (sheet
, mr
, GNM_CELL_COMMENT_TYPE
);
6217 r
.start
= r
.end
= *pos
;
6218 comments
= sheet_objects_get (sheet
, &r
, GNM_CELL_COMMENT_TYPE
);
6223 /* This assumes just one comment per cell. */
6224 res
= comments
->data
;
6225 g_slist_free (comments
);
6230 cb_find_extents (GnmCellIter
const *iter
, GnmCellPos
*extent
)
6232 if (extent
->col
< iter
->pp
.eval
.col
)
6233 extent
->col
= iter
->pp
.eval
.col
;
6234 if (extent
->row
< iter
->pp
.eval
.row
)
6235 extent
->row
= iter
->pp
.eval
.row
;
6241 * @sheet: sheet cells are contained on
6242 * @r: range to trim empty cells from
6243 * @cols: trim from right
6244 * @rows: trim from bottom
6246 * This removes empty rows/cols from the
6247 * right hand or bottom edges of the range
6248 * depending on the value of @cols or @rows.
6250 * Return value: TRUE if the range was totally empty.
6253 sheet_range_trim (Sheet
const *sheet
, GnmRange
*r
,
6254 gboolean cols
, gboolean rows
)
6256 GnmCellPos extent
= { -1, -1 };
6258 g_return_val_if_fail (IS_SHEET (sheet
), TRUE
);
6259 g_return_val_if_fail (r
!= NULL
, TRUE
);
6261 sheet_foreach_cell_in_range (
6262 (Sheet
*)sheet
, CELL_ITER_IGNORE_BLANK
,
6263 r
->start
.col
, r
->start
.row
, r
->end
.col
, r
->end
.row
,
6264 (CellIterFunc
) cb_find_extents
, &extent
);
6266 if (extent
.col
< 0 || extent
.row
< 0)
6269 r
->end
.col
= extent
.col
;
6271 r
->end
.row
= extent
.row
;
6276 * sheet_range_has_heading:
6277 * @sheet: Sheet to check
6278 * @src: GnmRange to check
6281 * Checks for a header row in @sheet!@src. If top is true it looks for a
6282 * header row from the top and if false it looks for a header col from the
6285 * Returns: TRUE if @src seems to have a heading
6288 sheet_range_has_heading (Sheet
const *sheet
, GnmRange
const *src
,
6289 gboolean top
, gboolean ignore_styles
)
6291 GnmCell
const *a
, *b
;
6294 /* There is only one row or col */
6296 if (src
->end
.row
<= src
->start
.row
)
6298 length
= src
->end
.col
- src
->start
.col
+ 1;
6300 if (src
->end
.col
<= src
->start
.col
)
6302 length
= src
->end
.row
- src
->start
.row
+ 1;
6305 for (i
= 0; i
< length
; i
++) {
6307 a
= sheet_cell_get (sheet
,
6308 src
->start
.col
+ i
, src
->start
.row
);
6309 b
= sheet_cell_get (sheet
,
6310 src
->start
.col
+ i
, src
->start
.row
+ 1);
6312 a
= sheet_cell_get (sheet
,
6313 src
->start
.col
, src
->start
.row
+ i
);
6314 b
= sheet_cell_get (sheet
,
6315 src
->start
.col
+ 1, src
->start
.row
+ i
);
6319 if (a
== NULL
|| a
->value
== NULL
|| b
== NULL
|| b
->value
== NULL
)
6322 if (VALUE_IS_NUMBER (a
->value
)) {
6323 if (!VALUE_IS_NUMBER (b
->value
))
6325 /* check for style differences */
6326 } else if (a
->value
->v_any
.type
!= b
->value
->v_any
.type
)
6329 /* Look for style differences */
6330 if (!ignore_styles
&&
6331 !gnm_style_equal_header (gnm_cell_get_style (a
),
6332 gnm_cell_get_style (b
), top
))
6340 * gnm_sheet_foreach_name:
6342 * @func: (scope call): #GHFunc
6345 * Executes @func for each name in @sheet.
6348 gnm_sheet_foreach_name (Sheet
const *sheet
, GHFunc func
, gpointer data
)
6350 g_return_if_fail (IS_SHEET (sheet
));
6353 gnm_named_expr_collection_foreach (sheet
->names
, func
, data
);
6357 * gnm_sheet_get_size:
6360 * Returns: (transfer none): the sheet size.
6362 GnmSheetSize
const *
6363 gnm_sheet_get_size (Sheet
const *sheet
)
6365 static const GnmSheetSize default_size
= {
6366 GNM_DEFAULT_COLS
, GNM_DEFAULT_ROWS
6369 if (G_UNLIKELY (!sheet
)) {
6370 g_warning ("NULL sheet in gnm_sheet_get_size!");
6371 /* FIXME: This needs to go. */
6372 return &default_size
;
6375 if (G_UNLIKELY (sheet
->being_constructed
))
6376 g_warning ("Access to sheet size during construction!");
6378 return &sheet
->size
;
6382 * gnm_sheet_get_size2:
6383 * @sheet: #Sheet, might be %NULL
6384 * @wb: #Workbook, must be non %NULL if @sheet is %NULL
6386 * Returns: (transfer none): the sheet size if @sheet is non %NULL, or the
6387 * default sheet size for @wb.
6389 GnmSheetSize
const *
6390 gnm_sheet_get_size2 (Sheet
const *sheet
, Workbook
const *wb
)
6393 ? gnm_sheet_get_size (sheet
)
6394 : workbook_get_sheet_size (wb
);
6398 gnm_sheet_set_solver_params (Sheet
*sheet
, GnmSolverParameters
*param
)
6400 g_return_if_fail (IS_SHEET (sheet
));
6401 g_return_if_fail (GNM_IS_SOLVER_PARAMETERS (param
));
6403 g_object_ref (param
);
6404 g_object_unref (sheet
->solver_parameters
);
6405 sheet
->solver_parameters
= param
;
6408 /* ------------------------------------------------------------------------- */
6411 * gnm_sheet_scenario_new:
6413 * @name: the new scenario name.
6415 * Returns: (transfer full): the newly created #GnmScenario.
6418 gnm_sheet_scenario_new (Sheet
*sheet
, const char *name
)
6423 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6424 g_return_val_if_fail (name
!= NULL
, NULL
);
6426 /* Check if a scenario having the same name already exists. */
6427 if (gnm_sheet_scenario_find (sheet
, name
)) {
6428 GString
*str
= g_string_new (NULL
);
6432 len
= strlen (name
);
6433 if (len
> 1 && name
[len
- 1] == ']') {
6434 for (i
= len
- 2; i
> 0; i
--) {
6435 if (! g_ascii_isdigit (name
[i
]))
6439 tmp
= g_strdup (name
);
6440 if (i
> 0 && name
[i
] == '[')
6443 tmp
= g_strdup (name
);
6445 for (j
= 1; ; j
++) {
6446 g_string_printf (str
, "%s [%d]", tmp
, j
);
6447 if (!gnm_sheet_scenario_find (sheet
, str
->str
)) {
6448 actual_name
= g_string_free (str
, FALSE
);
6454 g_string_free (str
, TRUE
);
6457 actual_name
= g_strdup (name
);
6459 sc
= gnm_scenario_new (actual_name
, sheet
);
6461 g_free (actual_name
);
6467 * gnm_sheet_scenario_find:
6469 * @name: the scenario name.
6471 * Returns: (transfer none): the newly created #GnmScenario.
6474 gnm_sheet_scenario_find (Sheet
*sheet
, const char *name
)
6478 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
6479 g_return_val_if_fail (name
!= NULL
, NULL
);
6481 for (l
= sheet
->scenarios
; l
; l
= l
->next
) {
6482 GnmScenario
*sc
= l
->data
;
6483 if (strcmp (name
, sc
->name
) == 0)
6491 * gnm_sheet_scenario_add:
6493 * @sc: (transfer full): #GnmScenario
6497 gnm_sheet_scenario_add (Sheet
*sheet
, GnmScenario
*sc
)
6499 g_return_if_fail (IS_SHEET (sheet
));
6500 g_return_if_fail (GNM_IS_SCENARIO (sc
));
6502 /* We take ownership of the ref. */
6503 sheet
->scenarios
= g_list_append (sheet
->scenarios
, sc
);
6507 gnm_sheet_scenario_remove (Sheet
*sheet
, GnmScenario
*sc
)
6509 g_return_if_fail (IS_SHEET (sheet
));
6510 g_return_if_fail (GNM_IS_SCENARIO (sc
));
6512 sheet
->scenarios
= g_list_remove (sheet
->scenarios
, sc
);
6513 g_object_unref (sc
);
6516 /* ------------------------------------------------------------------------- */
6519 * gnm_sheet_get_sort_setups:
6522 * Returns: (transfer none): the sort setups for @sheet.
6525 gnm_sheet_get_sort_setups (Sheet
*sheet
)
6527 GHashTable
*hash
= sheet
->sort_setups
;
6530 hash
= sheet
->sort_setups
=
6531 g_hash_table_new_full
6532 (g_str_hash
, g_str_equal
,
6533 g_free
, (GDestroyNotify
)gnm_sort_data_destroy
);
6539 gnm_sheet_add_sort_setup (Sheet
*sheet
, char *key
, gpointer setup
)
6541 GHashTable
*hash
= gnm_sheet_get_sort_setups (sheet
);
6543 g_hash_table_insert (hash
, key
, setup
);
6547 * gnm_sheet_find_sort_setup:
6551 * Returns: (transfer none): the found sort setup or %NULL.
6554 gnm_sheet_find_sort_setup (Sheet
*sheet
, char const *key
)
6556 if (sheet
->sort_setups
== NULL
)
6558 return g_hash_table_lookup (sheet
->sort_setups
, key
);