1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * colrow.c: Utilities for Rows and Columns
5 * Copyright (C) 1999-2007 Jody Goldberg (jody@gnome.org)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
22 #include <gnumeric-config.h>
27 #include "sheet-view.h"
28 #include "sheet-private.h"
29 #include "application.h"
30 #include "parse-util.h"
31 #include "selection.h"
33 #include "sheet-merge.h"
36 #include "rendered-value.h"
37 #include <goffice/goffice.h>
39 /* Making ColRowInfo a boxed type to make introspection happy. using no-op
40 * functions for copy and free, and crossing fingers.
43 col_row_info_copy (ColRowInfo
*cri
)
49 col_row_info_get_type (void)
54 t
= g_boxed_type_register_static ("ColRowInfo",
55 (GBoxedCopyFunc
)col_row_info_copy
,
56 (GBoxedFreeFunc
)col_row_info_copy
);
62 colrow_compute_pixel_scale (Sheet
const *sheet
, gboolean horizontal
)
64 return sheet
->last_zoom_factor_used
*
65 gnm_app_display_dpi_get (horizontal
) / 72.;
69 colrow_compute_pixels_from_pts (ColRowInfo
*cri
, Sheet
const *sheet
,
70 gboolean horizontal
, double scale
)
72 int const margin
= horizontal
? 2*GNM_COL_MARGIN
: 2*GNM_ROW_MARGIN
;
75 scale
= colrow_compute_pixel_scale (sheet
, horizontal
);
77 if (horizontal
&& sheet
->display_formulas
)
80 cri
->size_pixels
= (int)(cri
->size_pts
* scale
+ 0.5);
82 if (cri
->size_pixels
<= margin
)
83 cri
->size_pixels
= margin
+ 1;
87 colrow_compute_pts_from_pixels (ColRowInfo
*cri
, Sheet
const *sheet
,
88 gboolean horizontal
, double scale
)
91 scale
= colrow_compute_pixel_scale (sheet
, horizontal
);
93 if (horizontal
&& sheet
->display_formulas
)
96 cri
->size_pts
= cri
->size_pixels
/ scale
;
98 /* Disable this until we decide how to deal with scaling */
99 g_return_if_fail (cri
->size_pts
>= cri
->margin_a
+ cri
->margin_b
);
104 colrow_is_default (ColRowInfo
const *cri
)
106 g_return_val_if_fail (cri
!= NULL
, FALSE
);
107 return cri
->is_default
;
114 * TRUE if there is no information in col/row @cri.
117 colrow_is_empty (ColRowInfo
const *cri
)
121 return cri
->is_default
&&
122 cri
->outline_level
== 0 &&
123 !cri
->is_collapsed
&&
132 * Returns true if the infos are equivalent.
135 colrow_equal (ColRowInfo
const *a
, ColRowInfo
const *b
)
142 return fabs (a
->size_pts
- b
->size_pts
) < 1e-5 &&
143 a
->outline_level
== b
->outline_level
&&
144 a
->is_collapsed
== b
->is_collapsed
&&
145 a
->hard_size
== b
->hard_size
&&
146 a
->visible
== b
->visible
;
154 * Assign all content, except the position of @src to @dst
157 colrow_copy (ColRowInfo
*dst
, ColRowInfo
const *src
)
159 dst
->size_pts
= src
->size_pts
;
160 dst
->size_pixels
= src
->size_pixels
;
161 dst
->outline_level
= src
->outline_level
;
162 dst
->is_collapsed
= src
->is_collapsed
;
163 dst
->hard_size
= src
->hard_size
;
164 dst
->visible
= src
->visible
;
168 col_row_info_new (void)
170 return g_slice_new (ColRowInfo
);
174 colrow_free (ColRowInfo
*cri
)
176 g_slice_free1 (sizeof (*cri
), cri
);
181 * @infos: The Row or Column collection.
182 * @first: start position (inclusive)
183 * @last: stop column (inclusive)
184 * @callback: (scope call): A callback function which should return TRUE to stop
186 * @user_data: A bagage pointer.
188 * Iterates through the existing rows or columns within the range supplied.
189 * Currently only support left -> right iteration. If a callback returns
190 * TRUE iteration stops.
193 colrow_foreach (ColRowCollection
const *infos
, int first
, int last
,
194 ColRowHandler callback
, gpointer user_data
)
197 ColRowSegment
const *segment
;
198 int sub
, inner_last
, i
;
200 /* TODO : Do we need to support right -> left as an option */
203 if (last
> infos
->max_used
)
204 last
= infos
->max_used
;
206 for (i
= first
; i
<= last
; ) {
207 segment
= COLROW_GET_SEGMENT (infos
, i
);
208 sub
= COLROW_SUB_INDEX(i
);
209 inner_last
= (COLROW_SEGMENT_INDEX (last
) == COLROW_SEGMENT_INDEX (i
))
210 ? COLROW_SUB_INDEX (last
)+1 : COLROW_SEGMENT_SIZE
;
212 i
+= COLROW_SEGMENT_SIZE
- sub
;
216 for (; sub
< inner_last
; sub
++, iter
.pos
++) {
217 iter
.cri
= segment
->info
[sub
];
218 if (iter
.cri
!= NULL
&& (*callback
)(&iter
, user_data
))
225 /*****************************************************************************/
227 typedef struct _ColRowIndex
{
233 cb_colrow_index_counter (gpointer data
, gpointer user_data
)
235 ColRowIndex
*index
= data
;
236 gint
*count
= user_data
;
238 *count
+= index
->last
- index
->first
+ 1;
242 colrow_vis_list_length (ColRowVisList
*list
)
245 g_slist_foreach (list
, cb_colrow_index_counter
, &count
);
250 * colrow_state_group_destroy:
251 * @set: (transfer full): the group to destroy.
253 * Returns: (transfer none): %NULL.
256 colrow_state_group_destroy (ColRowStateGroup
*group
)
258 ColRowStateGroup
*ptr
;
259 for (ptr
= group
; ptr
!= NULL
; ptr
= ptr
->next
)
260 colrow_state_list_destroy (ptr
->data
);
261 g_slist_free (group
);
266 colrow_index_compare (ColRowIndex
const * a
, ColRowIndex
const * b
)
268 return a
->first
- b
->first
;
272 * colrow_index_list_to_string: Convert an index list into a string.
273 * The result must be freed by the caller.
274 * It will be something like : A-B, F-G
277 * @is_cols: Column index list or row index list?
278 * @is_single: If non-null this will be set to TRUE if there's only a single col/row involved.
281 colrow_index_list_to_string (ColRowIndexList
*list
, gboolean is_cols
, gboolean
*is_single
)
283 ColRowIndexList
*ptr
;
285 gboolean single
= TRUE
;
287 g_return_val_if_fail (list
!= NULL
, NULL
);
289 result
= g_string_new (NULL
);
290 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
) {
291 ColRowIndex
*index
= ptr
->data
;
294 g_string_append (result
, cols_name (index
->first
, index
->last
));
296 g_string_append (result
, rows_name (index
->first
, index
->last
));
298 if (index
->last
!= index
->first
)
302 g_string_append (result
, ", ");
314 * colrow_get_index_list:
317 * @list: (transfer full):
319 * Build an ordered list of pairs doing intelligent merging
320 * of overlapping regions.
322 * Returns: (transfer full): @list.
325 colrow_get_index_list (int first
, int last
, ColRowIndexList
*list
)
327 ColRowIndex
*tmp
, *prev
;
330 tmp
= g_new (ColRowIndex
, 1);
334 list
= g_list_insert_sorted (list
, tmp
,
335 (GCompareFunc
)&colrow_index_compare
);
338 for (ptr
= list
->next
; ptr
!= NULL
; ) {
341 /* at the end of existing segment or contained */
342 if (prev
->last
+1 >= tmp
->first
) {
343 GList
*next
= ptr
->next
;
344 if (prev
->last
< tmp
->last
)
345 prev
->last
= tmp
->last
;
346 list
= g_list_remove_link (list
, ptr
);
357 * colrow_index_list_copy:
358 * @list: #ColRowIndexList
360 * Returns: (transfer full):
363 colrow_index_list_copy (ColRowIndexList
*list
)
365 GList
*copy
= NULL
, *ptr
;
367 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
) {
368 ColRowIndex
*tmp
= g_new (ColRowIndex
, 1);
369 ColRowIndex
*ex
= ptr
->data
;
370 tmp
->first
= ex
->first
;
371 tmp
->last
= ex
->last
;
372 copy
= g_list_prepend (copy
, tmp
);
374 return g_list_reverse (copy
);
378 colrow_set_single_state (ColRowState
*state
,
379 Sheet
*sheet
, int i
, gboolean is_cols
)
381 ColRowInfo
const *info
= sheet_colrow_get_info (sheet
, i
, is_cols
);
382 state
->is_default
= colrow_is_default (info
);
383 state
->size_pts
= info
->size_pts
;
384 state
->outline_level
= info
->outline_level
;
385 state
->is_collapsed
= info
->is_collapsed
;
386 state
->hard_size
= info
->hard_size
;
387 state
->visible
= info
->visible
;
391 * colrow_state_list_destroy:
392 * @list: (transfer full): the list to destroy.
394 * Returns: (transfer none): %NULL.
397 colrow_state_list_destroy (ColRowStateList
*list
)
399 g_slist_free_full (list
, g_free
);
406 * @is_cols: %TRUE if columns.
410 * Returns: (transfer full):
413 colrow_get_states (Sheet
*sheet
, gboolean is_cols
, int first
, int last
)
415 ColRowStateList
*list
= NULL
;
416 ColRowRLEState
*rles
;
417 ColRowState run_state
;
420 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
421 g_return_val_if_fail (first
<= last
, NULL
);
423 colrow_set_single_state (&run_state
, sheet
, first
, is_cols
);
426 for (i
= first
+ 1; i
<= last
; ++i
) {
427 ColRowState cur_state
;
428 colrow_set_single_state (&cur_state
, sheet
, i
, is_cols
);
430 /* If state changed, start a new block */
431 if (cur_state
.is_default
!= run_state
.is_default
||
432 cur_state
.size_pts
!= run_state
.size_pts
||
433 cur_state
.outline_level
!= run_state
.outline_level
||
434 cur_state
.is_collapsed
!= run_state
.is_collapsed
||
435 cur_state
.hard_size
!= run_state
.hard_size
||
436 cur_state
.visible
!= run_state
.visible
) {
437 rles
= g_new (ColRowRLEState
, 1);
438 rles
->length
= run_length
;
439 rles
->state
= run_state
;
440 list
= g_slist_prepend (list
, rles
);
442 run_state
= cur_state
;
448 /* Store the final run */
449 rles
= g_new (ColRowRLEState
, 1);
450 rles
->length
= run_length
;
451 rles
->state
= run_state
;
452 list
= g_slist_prepend (list
, rles
);
454 return g_slist_reverse (list
);
457 struct resize_closure
{
464 cb_set_colrow_size (GnmColRowIter
const *iter
, gpointer userdata
)
466 if (iter
->cri
->visible
) {
467 struct resize_closure
const *c
= userdata
;
470 sheet_col_set_size_pixels (c
->sheet
, iter
->pos
,
473 sheet_row_set_size_pixels (c
->sheet
, iter
->pos
,
480 cb_clear_variable_width_content (GnmCellIter
const *iter
,
481 G_GNUC_UNUSED gpointer user
)
483 GnmRenderedValue
*rv
= gnm_cell_get_rendered_value (iter
->cell
);
484 if (rv
&& rv
->variable_width
) {
485 iter
->ri
->needs_respan
= TRUE
;
486 gnm_cell_unrender (iter
->cell
);
494 * @is_cols: %TRUE if columns.
498 * Returns: (transfer full):
501 colrow_get_sizes (Sheet
*sheet
, gboolean is_cols
,
502 ColRowIndexList
*src
, int new_size
)
504 ColRowStateGroup
*res
= NULL
;
505 ColRowIndexList
*ptr
;
507 for (ptr
= src
; ptr
!= NULL
; ptr
= ptr
->next
) {
508 ColRowIndex
const *index
= ptr
->data
;
509 res
= g_slist_prepend (res
, colrow_get_states (sheet
, is_cols
,
510 index
->first
, index
->last
));
512 if (new_size
> 0 && index
->first
== 0 &&
513 (index
->last
+1) >= colrow_max (is_cols
, sheet
)) {
514 ColRowRLEState
*rles
= g_new0 (ColRowRLEState
, 1);
516 rles
->length
= -1; /* Flag as changing the default */
519 rles
->state
.size_pts
= sheet_col_get_default_size_pts (sheet
);
521 rles
->state
.size_pts
= sheet_row_get_default_size_pts (sheet
);
523 /* Result is a magic 'default' record + >= 1 normal */
524 return g_slist_prepend (res
, g_slist_append (NULL
, rles
));
540 * Returns: (transfer full):
543 colrow_set_sizes (Sheet
*sheet
, gboolean is_cols
,
544 ColRowIndexList
*src
, int new_size
, int from
, int to
)
545 /* from & to are used to restrict fitting to that range. Pass 0, -1 if you want to use the */
546 /* whole row/column */
549 ColRowStateGroup
*res
= NULL
;
550 ColRowIndexList
*ptr
;
552 for (ptr
= src
; ptr
!= NULL
; ptr
= ptr
->next
) {
553 ColRowIndex
const *index
= ptr
->data
;
554 res
= g_slist_prepend (res
, colrow_get_states (sheet
, is_cols
,
555 index
->first
, index
->last
));
558 * If we are changing the size of more than half of the rows/col to
559 * something specific (not autosize) we should change the default
560 * row/col size instead. However, it is unclear how to handle
563 * we need better management of rows/cols. Currently if they are all
564 * defined calculation speed grinds to a halt.
566 if (new_size
> 0 && index
->first
== 0 &&
567 (index
->last
+1) >= colrow_max (is_cols
, sheet
)) {
568 struct resize_closure closure
;
569 ColRowRLEState
*rles
= g_new0 (ColRowRLEState
, 1);
571 rles
->length
= -1; /* Flag as changing the default */
573 closure
.sheet
= sheet
;
574 closure
.new_size
= new_size
;
575 closure
.is_cols
= is_cols
;
577 rles
->state
.size_pts
= sheet_col_get_default_size_pts (sheet
);
578 sheet_col_set_default_size_pixels (sheet
, new_size
);
579 colrow_foreach (&sheet
->cols
, 0, gnm_sheet_get_last_col (sheet
),
580 &cb_set_colrow_size
, &closure
);
582 rles
->state
.size_pts
= sheet_row_get_default_size_pts (sheet
);
583 sheet_row_set_default_size_pixels (sheet
, new_size
);
584 colrow_foreach (&sheet
->rows
, 0, gnm_sheet_get_last_row (sheet
),
585 &cb_set_colrow_size
, &closure
);
588 /* force a re-render of cells with expanding formats */
590 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
,
591 0, 0, gnm_sheet_get_last_col (sheet
), gnm_sheet_get_last_row (sheet
),
592 (CellIterFunc
) &cb_clear_variable_width_content
, NULL
);
594 /* Result is a magic 'default' record + >= 1 normal */
595 return g_slist_prepend (res
, g_slist_append (NULL
, rles
));
599 /* force a re-render of cells with expanding formats */
600 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
,
601 index
->first
, 0, index
->last
, gnm_sheet_get_last_row (sheet
),
602 (CellIterFunc
) &cb_clear_variable_width_content
, NULL
);
604 /* In order to properly reposition cell comments in
605 * merged cells that cross the boundary we need to do
606 * everything. Remove this when comments are handled
608 sheet
->priv
->reposition_objects
.col
= 0;
611 for (i
= index
->first
; i
<= index
->last
; ++i
) {
614 int max
= is_cols
? gnm_sheet_get_last_row (sheet
)
615 : gnm_sheet_get_last_col (sheet
);
618 if (to
< 0 || to
> max
)
622 /* Fall back to assigning the default if it is empty */
624 ? sheet_col_size_fit_pixels (sheet
, i
, from
, to
, FALSE
)
625 : sheet_row_size_fit_pixels (sheet
, i
, from
, to
, FALSE
);
629 sheet_col_set_size_pixels (sheet
, i
, tmp
, new_size
> 0);
631 sheet_row_set_size_pixels (sheet
, i
, tmp
, new_size
> 0);
632 } else if (sheet_colrow_get (sheet
, i
, is_cols
) != NULL
) {
634 sheet_col_set_size_pixels (sheet
, i
,
635 sheet_col_get_default_size_pixels (sheet
), FALSE
);
637 sheet_row_set_size_pixels (sheet
, i
,
638 sheet_row_get_default_size_pixels (sheet
), FALSE
);
647 * NOTE : this is a low level routine it does not redraw or
650 * NOTE : this does not delete states any longer since it may be used
651 * for several sheets.
655 colrow_set_states (Sheet
*sheet
, gboolean is_cols
,
656 int first
, ColRowStateList
*states
)
659 int i
, max_outline
, offset
= first
;
660 ColRowCollection
*infos
;
663 g_return_if_fail (IS_SHEET (sheet
));
665 infos
= is_cols
? &(sheet
->cols
) : &(sheet
->rows
);
666 max_outline
= infos
->max_outline_level
;
667 scale
= colrow_compute_pixel_scale (sheet
, is_cols
);
669 for (l
= states
; l
!= NULL
; l
= l
->next
) {
670 ColRowRLEState
const *rles
= l
->data
;
671 ColRowState
const *state
= &rles
->state
;
673 if (max_outline
< state
->outline_level
)
674 max_outline
= state
->outline_level
;
676 for (i
= offset
; i
< offset
+ rles
->length
; i
++) {
677 if (state
->is_default
) {
678 ColRowSegment
*segment
= COLROW_GET_SEGMENT(infos
, i
);
679 if (segment
!= NULL
) {
680 int const sub
= COLROW_SUB_INDEX (i
);
681 ColRowInfo
*cri
= segment
->info
[sub
];
683 segment
->info
[sub
] = NULL
;
688 ColRowInfo
*cri
= sheet_colrow_fetch (sheet
, i
, is_cols
);
689 cri
->hard_size
= state
->hard_size
;
690 cri
->size_pts
= state
->size_pts
;
691 colrow_compute_pixels_from_pts (cri
, sheet
, is_cols
, scale
);
692 colrow_set_outline (cri
, state
->outline_level
,
693 state
->is_collapsed
);
696 offset
+= rles
->length
;
699 /* Notify sheet of pending update */
700 sheet
->priv
->recompute_visibility
= TRUE
;
702 sheet_flag_recompute_spans (sheet
);
704 /* In order to properly reposition cell
705 * comments in merged cells that cross the
706 * boundary we need to do everything. Revert
707 * this when comments are handled properly */
709 if (sheet
->priv
->reposition_objects
.col
> first
)
710 sheet
->priv
->reposition_objects
.col
= first
;
712 sheet
->priv
->reposition_objects
.col
= 0;
715 if (sheet
->priv
->reposition_objects
.row
> first
)
716 sheet
->priv
->reposition_objects
.row
= first
;
718 sheet_colrow_gutter (sheet
, is_cols
, max_outline
);
722 colrow_restore_state_group (Sheet
*sheet
, gboolean is_cols
,
723 ColRowIndexList
*selection
,
724 ColRowStateGroup
*state_groups
)
726 ColRowStateGroup
*ptr
= state_groups
;
728 /* Cycle to end, we have to traverse the selections
729 * in parallel with the state_groups
731 selection
= g_list_last (selection
);
732 for (; selection
!= NULL
&& ptr
!= NULL
; ptr
= ptr
->next
) {
733 ColRowIndex
const *index
= selection
->data
;
734 ColRowStateList
*list
= ptr
->data
;
735 ColRowRLEState
const *rles
= list
->data
;
737 /* MAGIC : the -1 was set above to flag this */
738 if (rles
->length
== -1) {
740 sheet_col_set_default_size_pts (sheet
, rles
->state
.size_pts
);
742 sheet_row_set_default_size_pts (sheet
, rles
->state
.size_pts
);
744 /* we are guaranteed to have at least 1 more record */
748 colrow_set_states (sheet
, is_cols
, index
->first
, ptr
->data
);
749 /* force a re-render of cells with expanding formats */
751 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
,
752 index
->first
, 0, index
->last
, gnm_sheet_get_last_row (sheet
),
753 (CellIterFunc
) &cb_clear_variable_width_content
, NULL
);
754 selection
= selection
->prev
;
759 * rows_height_update:
761 * @range: The range whose rows should be resized.
762 * @shrink: If set to FALSE, rows will never shrink!
764 * Use this function having changed the font size to auto
765 * resize the row heights to make the text fit nicely.
768 rows_height_update (Sheet
*sheet
, GnmRange
const * range
, gboolean shrink
)
770 /* FIXME : this needs to check font sizes and contents rather than
771 * just contents. Empty cells will cause resize also */
772 colrow_autofit (sheet
, range
, FALSE
, FALSE
,
777 /* ------------------------------------------------------------------------- */
781 const GnmRange
*range
;
782 gboolean ignore_strings
;
783 gboolean min_current
;
784 gboolean min_default
;
788 cb_autofit_col (GnmColRowIter
const *iter
, gpointer data_
)
790 struct cb_autofit
*data
= data_
;
793 if (iter
->cri
->hard_size
)
796 size
= sheet_col_size_fit_pixels (data
->sheet
, iter
->pos
,
797 data
->range
->start
.row
, data
->range
->end
.row
,
798 data
->ignore_strings
);
799 /* FIXME: better idea than this? */
800 max
= 50 * sheet_col_get_default_size_pixels (data
->sheet
);
801 size
= MIN (size
, max
);
804 if (data
->min_current
)
805 min
= MAX (min
, iter
->cri
->size_pixels
);
806 if (data
->min_default
)
807 min
= MAX (min
, sheet_col_get_default_size_pixels (data
->sheet
));
810 sheet_col_set_size_pixels (data
->sheet
, iter
->pos
, size
, FALSE
);
816 cb_autofit_row (GnmColRowIter
const *iter
, gpointer data_
)
818 struct cb_autofit
*data
= data_
;
821 if (iter
->cri
->hard_size
)
824 size
= sheet_row_size_fit_pixels (data
->sheet
, iter
->pos
,
825 data
->range
->start
.col
, data
->range
->end
.col
,
826 data
->ignore_strings
);
827 max
= 20 * sheet_row_get_default_size_pixels (data
->sheet
);
828 size
= MIN (size
, max
);
831 if (data
->min_current
)
832 min
= MAX (min
, iter
->cri
->size_pixels
);
833 if (data
->min_default
)
834 min
= MAX (min
, sheet_row_get_default_size_pixels (data
->sheet
));
837 sheet_row_set_size_pixels (data
->sheet
, iter
->pos
, size
, FALSE
);
844 * @sheet: the sheet to change
845 * @range: the range to consider
846 * @is_cols: TRUE for columns, FALSE for rows.
847 * @ignore_strings: Don't consider cells with string values.
848 * @min_current: Don't shrink below current size.
849 * @min_default: Don't shrink below default size.
850 * @indices: indices appropriate for colrow_restore_state_group.
851 * @sizes: old sizes appropriate for colrow_restore_state_group.
853 * This function autofits columns or rows in @range as specified by
854 * @is_cols. Only cells in @range are considered for the sizing
855 * and the size can be bounded below by current size and/or default
859 colrow_autofit (Sheet
*sheet
, const GnmRange
*range
, gboolean is_cols
,
860 gboolean ignore_strings
,
861 gboolean min_current
, gboolean min_default
,
862 ColRowIndexList
**indices
,
863 ColRowStateGroup
**sizes
)
865 struct cb_autofit data
;
867 ColRowCollection
*crs
;
868 ColRowHandler handler
;
872 data
.ignore_strings
= ignore_strings
;
873 data
.min_current
= min_current
;
874 data
.min_default
= min_default
;
877 a
= range
->start
.col
;
880 handler
= cb_autofit_col
;
882 a
= range
->start
.row
;
885 handler
= cb_autofit_row
;
889 *indices
= colrow_get_index_list (a
, b
, NULL
);
891 *sizes
= g_slist_prepend (NULL
, colrow_get_states (sheet
, is_cols
, a
, b
));
893 /* We potentially do a lot of recalcs as part of this, so make sure
894 stuff that caches sub-computations see the whole thing instead
895 of clearing between cells. */
896 gnm_app_recalc_start ();
897 colrow_foreach (crs
, a
, b
, handler
, &data
);
898 gnm_app_recalc_finish ();
902 colrow_autofit_col (Sheet
*sheet
, GnmRange
*r
)
904 colrow_autofit (sheet
, r
, TRUE
, TRUE
,
905 TRUE
, FALSE
, NULL
, NULL
);
906 sheet_foreach_cell_in_range (sheet
, CELL_ITER_IGNORE_BLANK
,
908 r
->end
.col
, gnm_sheet_get_last_row (sheet
),
909 (CellIterFunc
) &cb_clear_variable_width_content
,
914 colrow_autofit_row (Sheet
*sheet
, GnmRange
*r
)
916 colrow_autofit (sheet
, r
, FALSE
, FALSE
,
917 TRUE
, FALSE
, NULL
, NULL
);
920 /*****************************************************************************/
924 gboolean is_cols
, visible
;
925 ColRowVisList
*elements
;
929 colrow_index_cmp (ColRowIndex
const *a
, ColRowIndex
const *b
)
931 /* We can be very simplistic here because the ranges never overlap */
932 return b
->first
- a
->first
;
936 colrow_visibility (Sheet
const *sheet
, ColRowVisiblity
* const dat
,
940 gboolean
const visible
= dat
->visible
;
941 ColRowInfo
* (*get
) (Sheet
const *sheet
, int pos
) = (dat
->is_cols
)
942 ? &sheet_col_get
: &sheet_row_get
;
944 /* Find the end of a segment that will be toggled */
945 for (i
= last
; i
>= first
; --i
) {
948 ColRowInfo
const *cri
= (*get
) (sheet
, i
);
953 } else if ((visible
!= 0) == (cri
->visible
!= 0))
956 /* Find the begining */
957 for (j
= i
; j
>= first
; --j
) {
958 cri
= (*get
) (sheet
, j
);
962 } else if ((visible
!= 0) == (cri
->visible
!= 0))
964 else if (cri
->is_collapsed
) {
969 res
= g_new (ColRowIndex
, 1);
970 res
->first
= (j
>= first
) ? j
+1 : first
;
973 g_printerr ("%d %d\n", res
->index
, res
->count
);
975 dat
->elements
= g_slist_insert_sorted (dat
->elements
, res
,
976 (GCompareFunc
)colrow_index_cmp
);
978 if (visible
&& cri
!= NULL
&& cri
->is_collapsed
) {
979 i
= colrow_find_outline_bound (
980 sheet
, dat
->is_cols
, j
,
981 cri
->outline_level
+1, FALSE
);
988 * colrow_get_outline_toggle:
995 * Returns: (transfer full):
998 colrow_get_outline_toggle (Sheet
const *sheet
, gboolean is_cols
, gboolean visible
,
1001 ColRowVisiblity closure
;
1002 closure
.is_cols
= is_cols
;
1003 closure
.visible
= visible
;
1004 closure
.elements
= NULL
;
1006 colrow_visibility (sheet
, &closure
, first
, last
);
1007 return closure
.elements
;
1011 cb_colrow_visibility (SheetView
*sv
, GnmRange
const *r
, gpointer closure
)
1013 ColRowVisiblity
* const dat
= (ColRowVisiblity
*)closure
;
1017 first
= r
->start
.col
;
1020 first
= r
->start
.row
;
1023 colrow_visibility (sv_sheet (sv
), dat
, first
, last
);
1027 * colrow_get_visiblity_toggle:
1028 * @sv: The sheet view whose selection we are interested in.
1029 * @is_cols: A flag indicating whether this it is a column or a row.
1030 * @visible: Should we unhide or hide the cols/rows.
1032 * Searches the selection list and generates a list of index,count
1033 * pairs of row/col ranges that need to be hidden or unhiden.
1035 * NOTE : leave sheet non-const until we have a const version of
1036 * sv_selection_apply.
1038 * Returns: (transfer full): the list.
1041 colrow_get_visiblity_toggle (SheetView
*sv
, gboolean is_cols
,
1044 ColRowVisiblity closure
;
1045 closure
.is_cols
= is_cols
;
1046 closure
.visible
= visible
;
1047 closure
.elements
= NULL
;
1049 sv_selection_apply (sv
, &cb_colrow_visibility
, FALSE
, &closure
);
1051 return closure
.elements
;
1055 * colrow_set_visibility_list :
1057 * This is the high level command that is wrapped by undo and redo.
1058 * It should not be called by other commands.
1061 colrow_set_visibility_list (Sheet
*sheet
, gboolean is_cols
,
1062 gboolean visible
, ColRowVisList
*list
)
1067 for (ptr
= list
; ptr
!= NULL
; ptr
= ptr
->next
) {
1069 colrow_set_visibility (sheet
, is_cols
, visible
,
1070 info
->first
, info
->last
);
1074 sheet_colrow_optimize (sheet
);
1077 sheet_queue_respan (sheet
, 0, gnm_sheet_get_last_row (sheet
));
1079 sheet_redraw_all (sheet
, TRUE
);
1083 * colrow_set_outline :
1084 * @cri: the col/row to tweak
1088 * Adjust the outline state of a col/row
1091 colrow_set_outline (ColRowInfo
*cri
, int outline_level
, gboolean is_collapsed
)
1093 g_return_if_fail (outline_level
>= 0);
1095 cri
->is_collapsed
= (is_collapsed
!= 0); /* be anal */
1096 cri
->outline_level
= outline_level
;
1100 * colrow_find_outline_bound :
1102 * find the next/prev col/row at the designated depth starting from the
1106 colrow_find_outline_bound (Sheet
const *sheet
, gboolean is_cols
,
1107 int index
, int depth
, gboolean inc
)
1109 ColRowInfo
* (*get
) (Sheet
const *sheet
, int pos
) = is_cols
1110 ? &sheet_col_get
: &sheet_row_get
;
1111 int const max
= colrow_max (is_cols
, sheet
);
1112 int const step
= inc
? 1 : -1;
1115 ColRowInfo
const *cri
;
1116 int const next
= index
+ step
;
1118 if (next
< 0 || next
>= max
)
1120 cri
= (*get
) (sheet
, next
);
1121 if (cri
== NULL
|| cri
->outline_level
< depth
)
1130 * colrow_set_visibility:
1132 * @is_cols: Are we dealing with rows or columns.
1133 * @visible: Make things visible or invisible.
1134 * @first: The index of the first row/col (inclusive)
1135 * @last: The index of the last row/col (inclusive)
1137 * Change the visibility of the selected range of contiguous cols/rows.
1138 * NOTE : only changes the collapsed state for the LAST+1 element.
1141 colrow_set_visibility (Sheet
*sheet
, gboolean is_cols
,
1142 gboolean visible
, int first
, int last
)
1144 int i
, step
, prev_outline
= 0;
1145 gboolean changed
= FALSE
;
1146 GnmRange
* const bound
= &sheet
->priv
->unhidden_region
;
1147 gboolean
const fwd
= is_cols
? sheet
->outline_symbols_right
: sheet
->outline_symbols_below
;
1149 g_return_if_fail (IS_SHEET (sheet
));
1150 g_return_if_fail (first
<= last
);
1152 if (visible
) { /* expand to include newly visible regions */
1154 if (bound
->start
.col
> first
)
1155 bound
->start
.col
= first
;
1156 if (bound
->end
.col
< last
)
1157 bound
->end
.col
= last
;
1159 if (bound
->start
.row
> first
)
1160 bound
->start
.row
= first
;
1161 if (bound
->end
.row
< last
)
1162 bound
->end
.row
= last
;
1164 } else { /* contract to exclude newly hidden regions */
1166 if (bound
->start
.col
>= first
&& bound
->start
.col
<= last
)
1167 bound
->start
.col
= last
+1;
1168 if (bound
->end
.col
<= last
&& bound
->end
.col
>= first
)
1169 bound
->end
.col
= first
-1;
1171 if (bound
->start
.row
>= first
&& bound
->start
.row
<= last
)
1172 bound
->start
.row
= last
+1;
1173 if (bound
->end
.row
<= last
&& bound
->end
.row
>= first
)
1174 bound
->end
.row
= first
-1;
1186 for (; fwd
? (i
<= last
) : (i
>= first
) ; i
+= step
) {
1187 ColRowInfo
* const cri
= sheet_colrow_fetch (sheet
, i
, is_cols
);
1189 if (changed
&& prev_outline
> cri
->outline_level
&& !visible
)
1190 cri
->is_collapsed
= FALSE
;
1192 changed
= (visible
== 0) != (cri
->visible
== 0);
1194 cri
->visible
= visible
;
1195 prev_outline
= cri
->outline_level
;
1196 sheet
->priv
->recompute_visibility
= TRUE
;
1199 sheet_flag_recompute_spans (sheet
);
1201 /* In order to properly reposition cell
1202 * comments in merged cells that cross the
1203 * boundary we need to do everything. Revert
1204 * this when comments are handled properly */
1206 if (sheet
->priv
->reposition_objects
.col
> i
)
1207 sheet
->priv
->reposition_objects
.col
= i
;
1209 sheet
->priv
->reposition_objects
.col
= 0;
1212 if (sheet
->priv
->reposition_objects
.row
> i
)
1213 sheet
->priv
->reposition_objects
.row
= i
;
1218 if (changed
&& 0 <= i
&& i
< colrow_max (is_cols
, sheet
)) {
1219 ColRowInfo
*cri
= sheet_colrow_get (sheet
, i
, is_cols
);
1220 if (!cri
&& !visible
&& prev_outline
> 0)
1221 cri
= sheet_colrow_fetch (sheet
, i
, is_cols
);
1223 if (cri
&& prev_outline
> cri
->outline_level
)
1224 cri
->is_collapsed
= !visible
;
1229 * colrow_get_global_outline :
1236 * Collect the set of visiblity changes required to change the visiblity of
1237 * all outlined columns such tach those > @depth are visible.
1240 colrow_get_global_outline (Sheet
const *sheet
, gboolean is_cols
, int depth
,
1241 ColRowVisList
**show
, ColRowVisList
**hide
)
1243 ColRowInfo
const *cri
;
1244 ColRowIndex
*prev
= NULL
;
1245 gboolean show_prev
= FALSE
;
1246 unsigned tmp
, prev_outline
= 0;
1247 int i
, max
= is_cols
? sheet
->cols
.max_used
: sheet
->rows
.max_used
;
1249 *show
= *hide
= NULL
;
1250 for (i
= 0; i
<= max
; i
++) {
1251 cri
= sheet_colrow_get (sheet
, i
, is_cols
);
1253 if (cri
== NULL
|| cri
->outline_level
== 0) {
1258 prev_outline
= cri
->outline_level
;
1260 /* see what sort of changes are necessary and do simple run
1261 * length encoding. Do not be too efficent, we need to change
1262 * the visiblity per outline level or the collapse state
1263 * change in colrow_set_visibility is missed. */
1264 if (cri
->outline_level
< depth
) {
1267 if (show_prev
&& prev
!= NULL
&& prev
->last
== (i
-1) &&
1268 tmp
== prev_outline
) {
1272 prev
= g_new (ColRowIndex
, 1);
1273 prev
->first
= prev
->last
= i
;
1274 *show
= g_slist_prepend (*show
, prev
);
1279 if (!show_prev
&& prev
!= NULL
&& prev
->last
== (i
-1) &&
1280 tmp
== prev_outline
) {
1284 prev
= g_new (ColRowIndex
, 1);
1285 prev
->first
= prev
->last
= i
;
1286 *hide
= g_slist_prepend (*hide
, prev
);
1291 *show
= g_slist_reverse (*show
);
1292 *hide
= g_slist_reverse (*hide
);
1296 colrow_resize (ColRowCollection
*infos
, int size
)
1298 int end_idx
= COLROW_SEGMENT_INDEX (size
);
1299 int i
= infos
->info
->len
- 1;
1301 while (i
>= end_idx
) {
1302 ColRowSegment
*segment
= g_ptr_array_index (infos
->info
, i
);
1305 g_ptr_array_index (infos
->info
, i
) = NULL
;
1310 g_ptr_array_set_size (infos
->info
, end_idx
);