1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * sheet-style.c: storage mechanism for styles and eventually cells.
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
6 * Copyright 2013 Morten Welinder (terra@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
25 #include "sheet-style.h"
26 #include "gnm-style-impl.h"
31 #include "style-border.h"
32 #include "style-color.h"
33 #include "style-conditions.h"
34 #include "parse-util.h"
37 #include <goffice/goffice.h>
38 #include <glib/gi18n-lib.h>
42 #define USE_TILE_POOLS 0
44 /* ------------------------------------------------------------------------- */
47 * This is, essentially, an std::multiset implementation for the style hash.
48 * Note, however, that sh_lookup is based on gnm_style_equal, not gnm_style_eq.
50 typedef GHashTable GnmStyleHash
;
53 /* This is a really crummy hash -- except for forcing collisions. */
54 #define gnm_style_hash(st) 0
58 sh_remove (GnmStyleHash
*h
, GnmStyle
*st
)
60 guint32 hv
= gnm_style_hash (st
);
61 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
63 g_return_if_fail (l
!= NULL
);
66 GSList
*next
= l
->next
;
68 /* We're removing the first of several elements. */
70 g_hash_table_replace (h
, GUINT_TO_POINTER (hv
), next
);
72 /* We're removing the last element. */
73 g_hash_table_remove (h
, GUINT_TO_POINTER (hv
));
76 /* We're removing an element that isn't first. */
77 l
= g_slist_remove (l
, st
);
82 sh_lookup (GnmStyleHash
*h
, GnmStyle
*st
)
84 guint32 hv
= gnm_style_hash (st
);
85 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
87 GnmStyle
*st2
= l
->data
;
88 /* NOTE: This uses gnm_style_equal, not gnm_style_eq. */
89 if (gnm_style_equal (st
, st2
))
97 sh_insert (GnmStyleHash
*h
, GnmStyle
*st
)
99 GSList
*s
= g_slist_prepend (NULL
, st
);
100 guint32 hv
= gnm_style_hash (st
);
101 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
106 g_hash_table_insert (h
, GUINT_TO_POINTER (hv
), s
);
111 sh_all_styles (GnmStyleHash
*h
)
117 g_hash_table_iter_init (&iter
, h
);
118 while (g_hash_table_iter_next (&iter
, NULL
, &value
)) {
120 for (; l
; l
= l
->next
)
121 res
= g_slist_prepend (res
, l
->data
);
127 static GnmStyleHash
*
130 return g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
131 NULL
, (GDestroyNotify
)g_slist_free
);
135 sh_destroy (GnmStyleHash
*h
)
137 g_hash_table_destroy (h
);
140 /* ------------------------------------------------------------------------- */
142 typedef union _CellTile CellTile
;
143 struct _GnmSheetStyleData
{
145 * style_hash is a set of all styles used by this sheet. These
146 * styles are all linked.
148 * We always re-use styles from here when we can, but there can
149 * still be duplicates. This happens when styles are changed
150 * while they are in the hash. For example, this happens when
151 * an expression used by a validation style changes due to
152 * row/col insert/delete.
154 GnmStyleHash
*style_hash
;
157 GnmStyle
*default_style
;
158 GnmColor
*auto_pattern_color
;
161 static gboolean debug_style_optimize
;
164 GnmSheetSize
const *ss
;
169 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
175 * For internal use only
178 sheet_style_unlink (Sheet
*sheet
, GnmStyle
*st
)
180 if (sheet
->style_data
->style_hash
)
181 sh_remove (sheet
->style_data
->style_hash
, st
);
186 * @sheet: (transfer full): the sheet
189 * Looks up a style from the sheets collection. Linking if necessary.
190 * ABSORBS the reference and adds a link.
192 * Returns: (transfer full): the new style.
195 sheet_style_find (Sheet
const *sheet
, GnmStyle
*s
)
198 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
200 gnm_style_link (res
);
205 s
= gnm_style_link_sheet (s
, (Sheet
*)sheet
);
207 /* Retry the lookup in case "s" changed. See #585178. */
208 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
210 gnm_style_link (res
);
212 * We are abandoning the linking here. We cannot use
213 * gnm_style_unlink as that would call sheet_style_unlink
214 * and thus remove "res" from the hash.
217 s
->linked_sheet
= NULL
;
223 sh_insert (sheet
->style_data
->style_hash
, s
);
227 /* Place holder until I merge in the new styles too */
229 pstyle_set_border (GnmStyle
*st
, GnmBorder
*border
,
230 GnmStyleBorderLocation side
)
232 gnm_style_set_border (st
,
233 GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (side
),
234 gnm_style_border_ref (border
));
237 /* Amortize the cost of applying a partial style over a large region
238 * by caching and rereferencing the merged result for repeated styles.
248 rstyle_ctor_style (ReplacementStyle
*res
, GnmStyle
*new_style
, Sheet
*sheet
)
251 res
->new_style
= sheet_style_find (sheet
, new_style
);
257 rstyle_ctor_pstyle (ReplacementStyle
*res
, GnmStyle
*pstyle
, Sheet
*sheet
)
260 res
->new_style
= NULL
;
261 res
->pstyle
= pstyle
;
262 res
->cache
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
266 cb_style_unlink (gpointer key
, gpointer value
, G_GNUC_UNUSED gpointer user_data
)
268 gnm_style_unlink ((GnmStyle
*)key
);
269 gnm_style_unlink ((GnmStyle
*)value
);
273 rstyle_dtor (ReplacementStyle
*rs
)
275 if (rs
->cache
!= NULL
) {
276 g_hash_table_foreach (rs
->cache
, cb_style_unlink
, NULL
);
277 g_hash_table_destroy (rs
->cache
);
280 if (rs
->new_style
!= NULL
) {
281 gnm_style_unlink (rs
->new_style
);
282 rs
->new_style
= NULL
;
284 if (rs
->pstyle
!= NULL
) {
285 gnm_style_unref (rs
->pstyle
);
291 * rstyle_apply: Utility routine that is at the core of applying partial
292 * styles or storing complete styles. It will eventually be smarter
293 * and will maintain the cache of styles associated with each sheet
296 rstyle_apply (GnmStyle
**old
, ReplacementStyle
*rs
, GnmRange
const *r
)
299 g_return_if_fail (old
!= NULL
);
300 g_return_if_fail (rs
!= NULL
);
302 if (rs
->pstyle
!= NULL
) {
303 /* Cache the merged styles keeping a reference to the originals
304 * just in case all instances change.
306 s
= g_hash_table_lookup (rs
->cache
, *old
);
308 GnmStyle
*tmp
= gnm_style_new_merged (*old
, rs
->pstyle
);
309 s
= sheet_style_find (rs
->sheet
, tmp
);
310 gnm_style_link (*old
);
311 g_hash_table_insert (rs
->cache
, *old
, s
);
318 gnm_style_unlink_dependents (*old
, r
);
319 gnm_style_unlink (*old
);
322 gnm_style_link_dependents (s
, r
);
330 sheet_style_clear_style_dependents (Sheet
*sheet
, GnmRange
const *r
)
332 GSList
*styles
= sh_all_styles (sheet
->style_data
->style_hash
);
333 g_slist_foreach (styles
,
334 (GFunc
)gnm_style_unlink_dependents
,
336 g_slist_free (styles
);
340 /****************************************************************************/
342 /* If you change this, change the tile_{widths,heights} here
343 * and GNM_MAX_COLS and GNM_MAX_ROWS in gnumeric.h */
344 #define TILE_TOP_LEVEL 6
346 #define TILE_SIZE_COL 8
347 #define TILE_SIZE_ROW 16
357 static int const tile_size
[/*type*/] = {
359 TILE_SIZE_COL
, /* TILE_COL */
360 TILE_SIZE_ROW
, /* TILE_ROW */
361 TILE_SIZE_COL
* TILE_SIZE_ROW
/* TILE_MATRIX */
363 static int const tile_col_count
[/*type*/] = {
365 TILE_SIZE_COL
, /* TILE_COL */
367 TILE_SIZE_COL
, /* TILE_MATRIX */
368 TILE_SIZE_COL
/* TILE_PTR_MATRIX */
370 static int const tile_row_count
[/*type*/] = {
373 TILE_SIZE_ROW
, /* TILE_ROW */
374 TILE_SIZE_ROW
, /* TILE_MATRIX */
375 TILE_SIZE_ROW
/* TILE_PTR_MATRIX */
377 static const char * const tile_type_str
[/*type*/] = {
378 "simple", "col", "row", "matrix", "ptr-matrix"
380 static int const tile_widths
[/*level*/] = {
383 TILE_SIZE_COL
* TILE_SIZE_COL
,
384 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
385 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
386 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
387 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
388 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
390 static int const tile_heights
[/*level*/] = {
393 TILE_SIZE_ROW
* TILE_SIZE_ROW
,
394 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
395 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
396 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
397 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
398 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
402 CellTileType
const type
;
404 } CellTileStyleSimple
;
406 CellTileType
const type
;
407 GnmStyle
*style
[TILE_SIZE_COL
];
410 CellTileType
const type
;
411 GnmStyle
*style
[TILE_SIZE_ROW
];
414 CellTileType
const type
;
415 GnmStyle
*style
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
416 } CellTileStyleMatrix
;
418 CellTileType
const type
;
419 CellTile
*ptr
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
423 CellTileType
const type
;
424 CellTileStyleSimple style_any
;
425 CellTileStyleSimple style_simple
;
426 CellTileStyleCol style_col
;
427 CellTileStyleRow style_row
;
428 CellTileStyleMatrix style_matrix
;
429 CellTilePtrMatrix ptr_matrix
;
432 static int active_sheet_count
;
434 static GOMemChunk
*tile_pools
[5];
435 #define CHUNK_ALLOC(T,ctt) ((T*)go_mem_chunk_alloc (tile_pools[(ctt)]))
436 #define CHUNK_FREE(ctt,v) go_mem_chunk_free (tile_pools[(ctt)], (v))
438 static const size_t tile_type_sizeof
[5] = {
439 sizeof (CellTileStyleSimple
),
440 sizeof (CellTileStyleCol
),
441 sizeof (CellTileStyleRow
),
442 sizeof (CellTileStyleMatrix
),
443 sizeof (CellTilePtrMatrix
)
445 static int tile_allocations
= 0;
447 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_slice_alloc (tile_type_sizeof[(ctt)]))
448 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_slice_free1 (tile_type_sizeof[(ctt)], (v)))
450 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_malloc (tile_type_sizeof[(ctt)]))
451 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_free ((v)))
457 * Destroy a CellTile (recursively if needed). This will unlink all the
458 * styles in it. We do _not_ unlink style dependents here. That is done
459 * only in rstyle_apply.
462 cell_tile_dtor (CellTile
*tile
)
466 g_return_if_fail (tile
!= NULL
);
469 if (t
== TILE_PTR_MATRIX
) {
470 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
472 cell_tile_dtor (tile
->ptr_matrix
.ptr
[i
]);
473 tile
->ptr_matrix
.ptr
[i
] = NULL
;
475 } else if (TILE_SIMPLE
<= t
&& t
<= TILE_MATRIX
) {
476 int i
= tile_size
[t
];
478 gnm_style_unlink (tile
->style_any
.style
[i
]);
479 tile
->style_any
.style
[i
] = NULL
;
482 g_return_if_fail (FALSE
); /* don't free anything */
485 *((CellTileType
*)&(tile
->type
)) = TILE_UNDEFINED
; /* poison it */
486 CHUNK_FREE (t
, tile
);
490 cell_tile_style_new (GnmStyle
*style
, CellTileType t
)
492 CellTile
*res
= CHUNK_ALLOC (CellTile
, t
);
493 *((CellTileType
*)&(res
->type
)) = t
;
496 int i
= tile_size
[t
];
497 gnm_style_link_multiple (style
, i
);
499 res
->style_any
.style
[i
] = style
;
506 cell_tile_ptr_matrix_new (CellTile
*t
)
508 CellTilePtrMatrix
*res
;
510 g_return_val_if_fail (t
!= NULL
, NULL
);
511 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
512 TILE_MATRIX
>= t
->type
, NULL
);
514 res
= CHUNK_ALLOC (CellTilePtrMatrix
, TILE_PTR_MATRIX
);
515 *((CellTileType
*)&(res
->type
)) = TILE_PTR_MATRIX
;
518 * If we wanted to get fancy we could use self similarity to decrease
519 * the number of subtiles. However, this would increase the cost of
520 * applying changes later so I'm not sure it is worth the effort.
524 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
526 res
->ptr
[i
] = cell_tile_style_new (
527 t
->style_simple
.style
[0], TILE_SIMPLE
);
532 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
533 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
534 res
->ptr
[i
++] = cell_tile_style_new (
535 t
->style_col
.style
[c
], TILE_SIMPLE
);
540 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
541 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
542 res
->ptr
[i
++] = cell_tile_style_new (
543 t
->style_row
.style
[r
], TILE_SIMPLE
);
547 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
549 res
->ptr
[i
] = cell_tile_style_new (
550 t
->style_matrix
.style
[i
], TILE_SIMPLE
);
556 return (CellTile
*)res
;
560 cell_tile_matrix_set (CellTile
*t
)
563 CellTileStyleMatrix
*res
;
565 g_return_val_if_fail (t
!= NULL
, NULL
);
566 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
567 TILE_MATRIX
>= t
->type
, NULL
);
569 if (t
->type
== TILE_MATRIX
)
572 res
= (CellTileStyleMatrix
*)cell_tile_style_new (NULL
, TILE_MATRIX
);
576 GnmStyle
*tmp
= t
->style_simple
.style
[0];
577 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
578 gnm_style_link_multiple (tmp
, i
);
586 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
)
587 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
588 gnm_style_link (res
->style
[i
++] =
589 t
->style_col
.style
[c
]);
595 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
) {
596 GnmStyle
*tmp
= t
->style_row
.style
[r
];
597 gnm_style_link_multiple (tmp
, TILE_SIZE_COL
);
598 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
599 res
->style
[i
++] = tmp
;
606 g_assert_not_reached();
611 return (CellTile
*)res
;
614 /****************************************************************************/
617 sheet_style_sanity_check (void)
622 for (c
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
623 g_assert (c
< G_MAXUINT
/ TILE_SIZE_COL
);
626 g_assert (c
>= GNM_MAX_COLS
);
628 for (r
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
629 g_assert (r
< G_MAXUINT
/ TILE_SIZE_COL
);
632 g_assert (r
>= GNM_MAX_ROWS
);
634 g_assert (G_N_ELEMENTS (tile_heights
) > TILE_TOP_LEVEL
+ 1);
636 g_assert (G_N_ELEMENTS (tile_widths
) > TILE_TOP_LEVEL
+ 1);
640 sheet_style_init_size (Sheet
*sheet
, int cols
, int rows
)
642 GnmStyle
*default_style
;
643 int lc
= 0, lr
= 0, w
= TILE_SIZE_COL
, h
= TILE_SIZE_ROW
;
653 sheet
->tile_top_level
= MAX (lc
, lr
);
655 if (active_sheet_count
++ == 0) {
657 tile_pools
[TILE_SIMPLE
] =
658 go_mem_chunk_new ("simple tile pool",
659 sizeof (CellTileStyleSimple
),
661 tile_pools
[TILE_COL
] =
662 go_mem_chunk_new ("column tile pool",
663 sizeof (CellTileStyleCol
),
665 tile_pools
[TILE_ROW
] =
666 go_mem_chunk_new ("row tile pool",
667 sizeof (CellTileStyleRow
),
669 tile_pools
[TILE_MATRIX
] =
670 go_mem_chunk_new ("matrix tile pool",
671 sizeof (CellTileStyleMatrix
),
672 MAX (16 * 1024 - 128,
673 100 * sizeof (CellTileStyleMatrix
)));
675 /* If this fails one day, just make two pools. */
676 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
677 tile_pools
[TILE_PTR_MATRIX
] = tile_pools
[TILE_MATRIX
];
681 sheet
->style_data
= g_new (GnmSheetStyleData
, 1);
682 sheet
->style_data
->style_hash
= sh_create ();
684 sheet
->style_data
->auto_pattern_color
= style_color_auto_pattern ();
686 default_style
= gnm_style_new_default ();
688 /* We can not do this, XL creates full page charts with background
689 * 'none' by default. Then displays that as white. */
690 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
691 gnm_style_set_back_color (default_style
,
692 gnm_color_new_rgb8 (0x50, 0x50, 0x50));
693 gnm_style_set_pattern (default_style
, 1);
696 sheet
->style_data
->default_style
=
697 sheet_style_find (sheet
, default_style
);
698 sheet
->style_data
->styles
=
699 cell_tile_style_new (sheet
->style_data
->default_style
,
704 sheet_style_init (Sheet
*sheet
)
706 int cols
= gnm_sheet_get_max_cols (sheet
);
707 int rows
= gnm_sheet_get_max_rows (sheet
);
709 debug_style_optimize
= gnm_debug_flag ("style-optimize");
711 sheet_style_sanity_check ();
713 sheet_style_init_size (sheet
, cols
, rows
);
717 sheet_style_resize (Sheet
*sheet
, int cols
, int rows
)
719 GnmStyleList
*styles
, *l
;
720 int old_cols
= gnm_sheet_get_max_cols (sheet
);
721 int old_rows
= gnm_sheet_get_max_rows (sheet
);
722 GnmRange save_range
, new_full
;
724 /* Save the style for the surviving area. */
725 range_init (&save_range
, 0, 0,
726 MIN (cols
, old_cols
) - 1, MIN (rows
, old_rows
) - 1);
727 styles
= sheet_style_get_range (sheet
, &save_range
);
729 /* Build new empty structures. */
730 sheet_style_shutdown (sheet
);
731 sheet_style_init_size (sheet
, cols
, rows
);
733 /* Reapply styles. */
734 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
735 for (l
= styles
; l
; l
= l
->next
) {
736 GnmStyleRegion
const *sr
= l
->data
;
737 GnmRange
const *r
= &sr
->range
;
738 GnmStyle
*style
= sr
->style
;
740 if (range_intersection (&newr
, r
, &new_full
)) {
741 gnm_style_ref (style
);
742 sheet_style_apply_range (sheet
, &newr
, style
);
746 style_list_free (styles
);
751 cb_tile_pool_leak (gpointer data
, gpointer user
)
753 CellTile
*tile
= data
;
754 g_printerr ("Leaking tile at %p.\n", (void *)tile
);
759 sheet_style_shutdown (Sheet
*sheet
)
764 g_return_if_fail (IS_SHEET (sheet
));
765 g_return_if_fail (sheet
->style_data
!= NULL
);
768 * Clear all styles. This is an easy way to clear out all
769 * style dependencies.
771 range_init_full_sheet (&r
, sheet
);
772 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
774 cell_tile_dtor (sheet
->style_data
->styles
);
775 sheet
->style_data
->styles
= NULL
;
777 sheet
->style_data
->default_style
= NULL
;
779 /* Clear the pointer to the hash BEFORE clearing and add a test in
780 * sheet_style_unlink. If we don't then it is possible/probable that
781 * unlinking the styles will attempt to remove them from the hash while
784 table
= sheet
->style_data
->style_hash
;
785 sheet
->style_data
->style_hash
= NULL
;
786 g_slist_free_full (sh_all_styles (table
),
787 (GDestroyNotify
)gnm_style_unlink
);
789 style_color_unref (sheet
->style_data
->auto_pattern_color
);
791 g_free (sheet
->style_data
);
792 sheet
->style_data
= NULL
;
794 if (--active_sheet_count
== 0) {
796 go_mem_chunk_foreach_leak (tile_pools
[TILE_SIMPLE
],
797 cb_tile_pool_leak
, NULL
);
798 go_mem_chunk_destroy (tile_pools
[TILE_SIMPLE
], FALSE
);
799 tile_pools
[TILE_SIMPLE
] = NULL
;
801 go_mem_chunk_foreach_leak (tile_pools
[TILE_COL
],
802 cb_tile_pool_leak
, NULL
);
803 go_mem_chunk_destroy (tile_pools
[TILE_COL
], FALSE
);
804 tile_pools
[TILE_COL
] = NULL
;
806 go_mem_chunk_foreach_leak (tile_pools
[TILE_ROW
],
807 cb_tile_pool_leak
, NULL
);
808 go_mem_chunk_destroy (tile_pools
[TILE_ROW
], FALSE
);
809 tile_pools
[TILE_ROW
] = NULL
;
811 go_mem_chunk_foreach_leak (tile_pools
[TILE_MATRIX
],
812 cb_tile_pool_leak
, NULL
);
813 go_mem_chunk_destroy (tile_pools
[TILE_MATRIX
], FALSE
);
814 tile_pools
[TILE_MATRIX
] = NULL
;
816 /* If this fails one day, just make two pools. */
817 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
818 tile_pools
[TILE_PTR_MATRIX
] = NULL
;
820 if (tile_allocations
)
821 g_printerr ("Leaking %d style tiles.\n", tile_allocations
);
827 * sheet_style_set_auto_pattern_color:
829 * @grid_color: (transfer full): The color
831 * Set the color for rendering auto colored patterns in this sheet.
832 * Absorbs a reference to @pattern_color;
835 sheet_style_set_auto_pattern_color (Sheet
*sheet
, GnmColor
*pattern_color
)
837 g_return_if_fail (IS_SHEET (sheet
));
838 g_return_if_fail (sheet
->style_data
!= NULL
);
840 style_color_unref (sheet
->style_data
->auto_pattern_color
);
841 sheet
->style_data
->auto_pattern_color
= gnm_color_new_auto (pattern_color
->go_color
);
842 style_color_unref (pattern_color
);
846 * sheet_style_get_auto_pattern_color:
849 * Returns: (transfer full): the color for rendering auto colored patterns
853 sheet_style_get_auto_pattern_color (Sheet
const *sheet
)
856 g_return_val_if_fail (IS_SHEET (sheet
), style_color_black ());
857 g_return_val_if_fail (sheet
->style_data
!= NULL
, style_color_black ());
858 g_return_val_if_fail (sheet
->style_data
->auto_pattern_color
!= NULL
,
859 style_color_black ());
861 sc
= sheet
->style_data
->auto_pattern_color
;
862 style_color_ref (sc
);
868 * sheet_style_update_grid_color:
870 * This function updates the color of gnm_style_border_none when the sheet to be
871 * rendered is known. gnm_style_border_none tells how to render the
872 * grid. Because the grid color may be different for different sheets, the
873 * functions which render the grid call this function first. The rule for
874 * selecting the grid color, which is the same as in Excel, is: - if the
875 * auto pattern color is default (which is black), the grid color is gray,
876 * as returned by style_color_grid (). - otherwise, the auto pattern color
877 * is used for the grid.
880 sheet_style_update_grid_color (Sheet
const *sheet
)
882 GnmColor
*default_auto
= style_color_auto_pattern ();
883 GnmColor
*sheet_auto
= sheet_style_get_auto_pattern_color (sheet
);
884 GnmColor
*grid_color
= style_color_grid ();
887 new_color
= (style_color_equal (default_auto
, sheet_auto
)
888 ? grid_color
: sheet_auto
);
890 /* Do nothing if we already have the right color */
891 if (gnm_style_border_none()->color
!= new_color
) {
892 style_color_ref (new_color
); /* none_set eats the ref */
893 gnm_style_border_none_set_color (new_color
);
895 style_color_unref (grid_color
);
896 style_color_unref (sheet_auto
);
897 style_color_unref (default_auto
);
900 /****************************************************************************/
903 tile_is_uniform (CellTile
const *tile
)
905 const int s
= tile_size
[tile
->type
];
906 GnmStyle
const *st
= tile
->style_any
.style
[0];
909 for (i
= 1; i
< s
; i
++)
910 if (tile
->style_any
.style
[i
] != st
)
917 vector_apply_pstyle (CellTile
*tile
, ReplacementStyle
*rs
,
918 int cc
, int cr
, int level
, GnmRange
const *indic
)
920 const CellTileType type
= tile
->type
;
921 const int ncols
= tile_col_count
[type
];
922 const int nrows
= tile_row_count
[type
];
923 const int w1
= tile_widths
[level
+ 1] / ncols
;
924 const int h1
= tile_heights
[level
+ 1] / nrows
;
925 const int fcol
= indic
->start
.col
;
926 const int frow
= indic
->start
.row
;
927 const int lcol
= MIN (ncols
- 1, indic
->end
.col
);
928 const int lrow
= MIN (nrows
- 1, indic
->end
.row
);
929 GnmSheetSize
const *ss
= gnm_sheet_get_size (rs
->sheet
);
933 for (r
= frow
; r
<= lrow
; r
++) {
934 GnmStyle
**st
= tile
->style_any
.style
+ ncols
* r
;
935 rng
.start
.row
= cr
+ h1
* r
;
936 rng
.end
.row
= MIN (rng
.start
.row
+ (h1
- 1),
938 for (c
= fcol
; c
<= lcol
; c
++) {
939 rng
.start
.col
= cc
+ w1
* c
;
940 rng
.end
.col
= MIN (rng
.start
.col
+ (w1
- 1),
942 rstyle_apply (st
+ c
, rs
, &rng
);
948 * Determine whether before applying a style in the area of apply_to
949 * one needs to split the tile column-wise.
951 * If FALSE is returned then the tile need to be split to a TILE_PTR_MATRIX
952 * because the current level is not fine-grained enough.
954 * If TRUE is returned, TILE_SIMPLE needs to be split into TILE_COL and
955 * TILE_ROW needs to be split into TILE_MATRIX. TILE_COL and TILE_MATRIX
956 * should be kept. In indic, the inclusive post-split indicies of the
957 * range will be returned.
959 * If apply_to covers the entire tile, TRUE will be returned and the judgement
960 * on splitting above should be ignored. The indices in indic will be as-if
961 * the split was done.
964 col_indicies (int corner_col
, int w
, GnmRange
const *apply_to
,
969 i
= apply_to
->start
.col
- corner_col
;
971 indec
->start
.col
= 0;
976 indec
->start
.col
= tmp
;
979 i
= 1 + apply_to
->end
.col
- corner_col
;
981 if (tmp
>= TILE_SIZE_COL
)
982 indec
->end
.col
= TILE_SIZE_COL
- 1;
986 indec
->end
.col
= tmp
- 1;
992 /* See docs for col_indicies. Swap cols and rows. */
994 row_indicies (int corner_row
, int h
, GnmRange
const *apply_to
,
999 i
= apply_to
->start
.row
- corner_row
;
1001 indic
->start
.row
= 0;
1006 indic
->start
.row
= tmp
;
1009 i
= 1 + apply_to
->end
.row
- corner_row
;
1011 if (tmp
>= TILE_SIZE_ROW
)
1012 indic
->end
.row
= TILE_SIZE_ROW
- 1;
1016 indic
->end
.row
= tmp
- 1;
1023 * cell_tile_apply: This is the primary logic for making changing areas in the
1024 * tree. It could be further optimised if it becomes a bottle neck.
1027 cell_tile_apply (CellTile
**tile
, int level
,
1028 int corner_col
, int corner_row
,
1029 GnmRange
const *apply_to
,
1030 ReplacementStyle
*rs
)
1032 int const width
= tile_widths
[level
+1];
1033 int const height
= tile_heights
[level
+1];
1034 int const w
= tile_widths
[level
];
1035 int const h
= tile_heights
[level
];
1036 gboolean
const full_width
= (apply_to
->start
.col
<= corner_col
&&
1037 apply_to
->end
.col
>= (corner_col
+width
-1));
1038 gboolean
const full_height
= (apply_to
->start
.row
<= corner_row
&&
1039 apply_to
->end
.row
>= (corner_row
+height
-1));
1044 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1045 g_return_if_fail (tile
!= NULL
);
1046 g_return_if_fail (*tile
!= NULL
);
1048 type
= (*tile
)->type
;
1049 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1051 /* applying the same style to part of a simple-tile is a nop */
1052 if (type
== TILE_SIMPLE
&&
1053 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1057 * Indices for the whole tile assuming a split to matrix.
1058 * We can still use these indices if we don't split either way.
1060 indic
.start
.col
= 0;
1061 indic
.start
.row
= 0;
1062 indic
.end
.col
= TILE_SIZE_COL
- 1;
1063 indic
.end
.row
= TILE_SIZE_ROW
- 1;
1065 if (type
== TILE_PTR_MATRIX
)
1067 else if (full_width
&& full_height
)
1069 else if (full_height
) {
1070 if (!col_indicies (corner_col
, w
, apply_to
, &indic
))
1071 goto split_to_ptr_matrix
;
1077 res
= cell_tile_style_new (
1078 (*tile
)->style_simple
.style
[0],
1080 cell_tile_dtor (*tile
);
1088 goto split_to_matrix
;
1090 g_assert_not_reached ();
1092 } else if (full_width
) {
1093 if (!row_indicies (corner_row
, h
, apply_to
, &indic
))
1094 goto split_to_ptr_matrix
;
1100 res
= cell_tile_style_new (
1101 (*tile
)->style_simple
.style
[0],
1103 cell_tile_dtor (*tile
);
1111 goto split_to_matrix
;
1113 g_assert_not_reached ();
1116 if (col_indicies (corner_col
, w
, apply_to
, &indic
) &&
1117 row_indicies (corner_row
, h
, apply_to
, &indic
))
1118 goto split_to_matrix
;
1120 goto split_to_ptr_matrix
;
1123 g_assert_not_reached ();
1126 *tile
= cell_tile_matrix_set (*tile
);
1129 vector_apply_pstyle (*tile
, rs
, corner_col
, corner_row
, level
, &indic
);
1133 CellTileOptimize cto
;
1134 cto
.ss
= gnm_sheet_get_size (rs
->sheet
);
1135 cto
.recursion
= FALSE
;
1136 cell_tile_optimize (tile
, level
, &cto
, corner_col
, corner_row
);
1140 split_to_ptr_matrix
:
1142 * We get here when apply_to's corners are not on a TILE_MATRIX grid.
1143 * Split to pointer matrix whose element tiles will have a finer grid.
1145 g_return_if_fail (type
!= TILE_PTR_MATRIX
);
1147 CellTile
*res
= cell_tile_ptr_matrix_new (*tile
);
1148 cell_tile_dtor (*tile
);
1150 type
= TILE_PTR_MATRIX
;
1154 g_return_if_fail (type
== TILE_PTR_MATRIX
);
1155 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1156 int const cr
= corner_row
+ h
*r
;
1157 if (cr
> apply_to
->end
.row
)
1159 if ((cr
+ h
) <= apply_to
->start
.row
)
1162 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1163 int const cc
= corner_col
+ w
*c
;
1164 if (cc
> apply_to
->end
.col
)
1166 if ((cc
+ w
) <= apply_to
->start
.col
)
1169 cell_tile_apply ((*tile
)->ptr_matrix
.ptr
+ i
+ c
,
1170 level
- 1, cc
, cr
, apply_to
, rs
);
1176 /* Handler for foreach_tile.
1178 * "width" and "height" refer to tile size which may extend beyond
1179 * the range supplied to foreach_tile and even beyond the sheet.
1181 typedef void (*ForeachTileFunc
) (GnmStyle
*style
,
1182 int corner_col
, int corner_row
,
1183 int width
, int height
,
1184 GnmRange
const *apply_to
, gpointer user
);
1186 foreach_tile_r (CellTile
*tile
, int level
,
1187 int corner_col
, int corner_row
,
1188 GnmRange
const *apply_to
,
1189 ForeachTileFunc handler
,
1192 int const width
= tile_widths
[level
+1];
1193 int const height
= tile_heights
[level
+1];
1194 int const w
= tile_widths
[level
];
1195 int const h
= tile_heights
[level
];
1198 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1199 g_return_if_fail (tile
!= NULL
);
1201 switch (tile
->type
) {
1203 handler (tile
->style_simple
.style
[0],
1204 corner_col
, corner_row
, width
, height
,
1209 if (apply_to
!= NULL
) {
1210 c
= (apply_to
->start
.col
- corner_col
) / w
;
1213 last
= (apply_to
->end
.col
- corner_col
) / w
+ 1;
1214 if (last
> TILE_SIZE_COL
)
1215 last
= TILE_SIZE_COL
;
1218 last
= TILE_SIZE_COL
;
1220 for (; c
< last
; ++c
)
1221 handler (tile
->style_col
.style
[c
],
1222 corner_col
+ c
*w
, corner_row
, w
, height
,
1227 if (apply_to
!= NULL
) {
1228 r
= (apply_to
->start
.row
- corner_row
) / h
;
1231 last
= (apply_to
->end
.row
- corner_row
) / h
+ 1;
1232 if (last
> TILE_SIZE_ROW
)
1233 last
= TILE_SIZE_ROW
;
1236 last
= TILE_SIZE_ROW
;
1238 for (; r
< last
; ++r
)
1239 handler (tile
->style_row
.style
[r
],
1240 corner_col
, corner_row
+ r
*h
, width
, h
,
1245 case TILE_PTR_MATRIX
:
1246 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1247 int const cr
= corner_row
+ h
*r
;
1249 if (cr
> apply_to
->end
.row
)
1251 if ((cr
+ h
) <= apply_to
->start
.row
)
1255 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1256 int const cc
= corner_col
+ w
*c
;
1258 if (cc
> apply_to
->end
.col
)
1260 if ((cc
+ w
) <= apply_to
->start
.col
)
1264 if (tile
->type
== TILE_MATRIX
) {
1265 handler (tile
->style_matrix
.style
[r
*TILE_SIZE_COL
+c
],
1268 w
, h
, apply_to
, user
);
1271 tile
->ptr_matrix
.ptr
[c
+ r
*TILE_SIZE_COL
],
1272 level
-1, cc
, cr
, apply_to
, handler
, user
);
1279 g_warning ("Adaptive Quad Tree corruption !");
1284 foreach_tile (Sheet
const *sheet
, GnmRange
const *apply_to
,
1285 ForeachTileFunc handler
, gpointer user
)
1287 foreach_tile_r (sheet
->style_data
->styles
,
1288 sheet
->tile_top_level
, 0, 0,
1289 apply_to
, handler
, user
);
1293 * cell_tile_apply_pos: This is an simplified version of cell_tile_apply. It
1294 * does not need all the bells and whistles because it operates on single cells.
1297 cell_tile_apply_pos (CellTile
**tile
, int level
,
1299 ReplacementStyle
*rs
)
1305 g_return_if_fail (col
>= 0);
1306 g_return_if_fail (col
< gnm_sheet_get_max_cols (rs
->sheet
));
1307 g_return_if_fail (row
>= 0);
1308 g_return_if_fail (row
< gnm_sheet_get_max_rows (rs
->sheet
));
1310 range_init (&rng
, col
, row
, col
, row
);
1313 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1314 g_return_if_fail (tile
!= NULL
);
1315 g_return_if_fail (*tile
!= NULL
);
1319 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1322 int const w
= tile_widths
[level
];
1323 int const c
= col
/ w
;
1324 int const h
= tile_heights
[level
];
1325 int const r
= row
/ h
;
1327 if (type
!= TILE_PTR_MATRIX
) {
1328 /* applying the same style to part of a simple-tile is a nop */
1329 if (type
== TILE_SIMPLE
&&
1330 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1333 tmp
= cell_tile_ptr_matrix_new (tmp
);
1334 cell_tile_dtor (*tile
);
1337 tile
= tmp
->ptr_matrix
.ptr
+ r
* TILE_SIZE_COL
+ c
;
1341 goto tail_recursion
;
1342 } else if (type
!= TILE_MATRIX
)
1343 *tile
= tmp
= cell_tile_matrix_set (tmp
);
1345 g_return_if_fail (tmp
->type
== TILE_MATRIX
);
1346 rstyle_apply (tmp
->style_matrix
.style
+ row
* TILE_SIZE_COL
+ col
,
1352 * sheet_style_set_range:
1357 * Change the complete style for a region.
1358 * This function absorbs a reference to the new @style.
1361 sheet_style_set_range (Sheet
*sheet
, GnmRange
const *range
,
1364 ReplacementStyle rs
;
1367 g_return_if_fail (IS_SHEET (sheet
));
1368 g_return_if_fail (range
!= NULL
);
1370 if (range
->start
.col
> range
->end
.col
||
1371 range
->start
.row
> range
->end
.row
) {
1372 gnm_style_unref (style
);
1377 range_ensure_sanity (&r
, sheet
);
1379 rstyle_ctor_style (&rs
, style
, sheet
);
1380 cell_tile_apply (&sheet
->style_data
->styles
,
1381 sheet
->tile_top_level
, 0, 0,
1387 * sheet_style_apply_col:
1392 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1393 * will make life easier.
1395 * Apply a partial style to a full col.
1396 * The routine absorbs a reference to the partial style.
1399 sheet_style_apply_col (Sheet
*sheet
, int col
, GnmStyle
*pstyle
)
1402 range_init_cols (&r
, sheet
, col
, col
);
1403 sheet_style_apply_range (sheet
, &r
, pstyle
);
1407 * sheet_style_apply_row:
1412 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1413 * will make life easier.
1415 * Apply a partial style to a full col.
1416 * The routine absorbs a reference to the partial style.
1419 sheet_style_apply_row (Sheet
*sheet
, int row
, GnmStyle
*pstyle
)
1422 range_init_rows (&r
, sheet
, row
, row
);
1423 sheet_style_apply_range (sheet
, &r
, pstyle
);
1427 * sheet_style_apply_pos:
1433 * Apply a partial style to a single cell
1434 * This function absorbs a reference to the new @style.
1437 sheet_style_apply_pos (Sheet
*sheet
, int col
, int row
,
1440 ReplacementStyle rs
;
1442 g_return_if_fail (IS_SHEET (sheet
));
1444 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1445 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1446 sheet
->tile_top_level
, col
, row
,
1451 * sheet_style_set_pos:
1457 * Change the complete style for a single cell.
1458 * This function absorbs a reference to the new @style.
1461 sheet_style_set_pos (Sheet
*sheet
, int col
, int row
,
1464 ReplacementStyle rs
;
1466 g_return_if_fail (IS_SHEET (sheet
));
1468 rstyle_ctor_style (&rs
, style
, sheet
);
1469 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1470 sheet
->tile_top_level
, col
, row
,
1476 * sheet_style_default:
1479 * Returns a reference to default style for a sheet.
1482 sheet_style_default (Sheet
const *sheet
)
1484 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1485 g_return_val_if_fail (sheet
->style_data
!= NULL
, NULL
);
1487 gnm_style_ref (sheet
->style_data
->default_style
);
1488 return sheet
->style_data
->default_style
;
1497 * Find the fully qualified style applicable to the specified cellpos.
1498 * Does _not_ add a reference.
1501 sheet_style_get (Sheet
const *sheet
, int col
, int row
)
1503 int level
= sheet
->tile_top_level
;
1504 CellTile
*tile
= sheet
->style_data
->styles
;
1507 int width
= tile_widths
[level
];
1508 int height
= tile_heights
[level
];
1509 int c
= col
/ width
;
1510 int r
= row
/ height
;
1512 g_return_val_if_fail (tile
!= NULL
, NULL
);
1513 g_return_val_if_fail (0 <= c
&& c
< TILE_SIZE_COL
, NULL
);
1514 g_return_val_if_fail (0 <= r
&& r
< TILE_SIZE_ROW
, NULL
);
1516 switch (tile
->type
) {
1518 return tile
->style_simple
.style
[0];
1520 return tile
->style_col
.style
[c
];
1522 return tile
->style_row
.style
[r
];
1524 return tile
->style_matrix
.style
[r
* TILE_SIZE_COL
+ c
];
1526 case TILE_PTR_MATRIX
:
1527 g_return_val_if_fail (level
> 0, NULL
);
1530 tile
= tile
->ptr_matrix
.ptr
[r
* TILE_SIZE_COL
+ c
];
1536 g_warning ("Adaptive Quad Tree corruption !");
1542 #define border_null(b) ((b) == none || (b) == NULL)
1545 style_row (GnmStyle
*style
, int start_col
, int end_col
, GnmStyleRow
*sr
, gboolean accept_conditions
)
1547 GnmBorder
const *top
, *bottom
, *none
= gnm_style_border_none ();
1548 GnmBorder
const *left
, *right
, *v
;
1549 int const end
= MIN (end_col
, sr
->end_col
);
1550 int i
= MAX (start_col
, sr
->start_col
);
1552 if (accept_conditions
&& style
->conditions
) {
1556 for (eval_pos_init (&ep
, (Sheet
*)sr
->sheet
, i
, sr
->row
); ep
.eval
.col
<= end
; ep
.eval
.col
++) {
1557 res
= gnm_style_conditions_eval (style
->conditions
, &ep
);
1558 style_row (res
>= 0 ? g_ptr_array_index (style
->cond_styles
, res
) : style
,
1559 ep
.eval
.col
, ep
.eval
.col
, sr
, FALSE
);
1564 top
= gnm_style_get_border (style
, MSTYLE_BORDER_TOP
);
1565 bottom
= gnm_style_get_border (style
, MSTYLE_BORDER_BOTTOM
);
1566 left
= gnm_style_get_border (style
, MSTYLE_BORDER_LEFT
);
1567 right
= gnm_style_get_border (style
, MSTYLE_BORDER_RIGHT
);
1569 /* Cancel grids if there is a background */
1570 if (sr
->hide_grid
|| gnm_style_get_pattern (style
) > 0) {
1581 if (left
!= none
&& border_null (sr
->vertical
[i
]))
1582 sr
->vertical
[i
] = left
;
1583 v
= border_null (right
) ? left
: right
;
1586 sr
->styles
[i
] = style
;
1587 if (top
!= none
&& border_null (sr
->top
[i
]))
1589 sr
->bottom
[i
] = bottom
;
1590 sr
->vertical
[++i
] = v
;
1592 if (border_null (right
))
1593 sr
->vertical
[i
] = right
;
1597 get_style_row (CellTile
const *tile
, int level
,
1598 int corner_col
, int corner_row
,
1601 int const width
= tile_widths
[level
+1];
1602 int const w
= tile_widths
[level
];
1603 int const h
= tile_heights
[level
];
1607 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1608 g_return_if_fail (tile
!= NULL
);
1612 if (t
!= TILE_SIMPLE
&& t
!= TILE_COL
) {
1613 r
= (sr
->row
> corner_row
) ? (sr
->row
- corner_row
)/ h
: 0;
1614 g_return_if_fail (r
< TILE_SIZE_ROW
);
1617 if (t
== TILE_ROW
|| t
== TILE_SIMPLE
) {
1618 style_row (tile
->style_any
.style
[r
],
1619 corner_col
, corner_col
+ width
- 1, sr
, TRUE
);
1621 /* find the start and end */
1623 int last_c
= (sr
->end_col
- corner_col
) / w
;
1624 if (last_c
>= TILE_SIZE_COL
)
1625 last_c
= TILE_SIZE_COL
-1;
1626 if (sr
->start_col
> corner_col
) {
1627 c
= (sr
->start_col
- corner_col
) / w
;
1628 corner_col
+= c
* w
;
1634 if (t
!= TILE_PTR_MATRIX
) {
1635 GnmStyle
* const *styles
= tile
->style_any
.style
+ r
*TILE_SIZE_COL
;
1637 for ( ; c
<= last_c
; c
++, corner_col
+= w
)
1638 style_row (styles
[c
],
1639 corner_col
, corner_col
+ w
- 1, sr
, TRUE
);
1641 CellTile
* const *tiles
= tile
->ptr_matrix
.ptr
+ r
*TILE_SIZE_COL
;
1643 g_return_if_fail (level
> 0);
1645 for ( level
-- ; c
<= last_c
; c
++, corner_col
+= w
)
1646 get_style_row (tiles
[c
], level
,
1647 corner_col
, corner_row
, sr
);
1653 * sheet_style_get_row:
1657 * A utility routine which efficiently retrieves a range of styles within a row.
1658 * It also merges adjacent borders as necessary.
1661 sheet_style_get_row (Sheet
const *sheet
, GnmStyleRow
*sr
)
1664 g_return_if_fail (IS_SHEET (sheet
));
1665 g_return_if_fail (sr
!= NULL
);
1666 g_return_if_fail (sr
->styles
!= NULL
);
1667 g_return_if_fail (sr
->vertical
!= NULL
);
1668 g_return_if_fail (sr
->top
!= NULL
);
1669 g_return_if_fail (sr
->bottom
!= NULL
);
1672 sr
->vertical
[sr
->start_col
] = gnm_style_border_none ();
1673 get_style_row (sheet
->style_data
->styles
, sheet
->tile_top_level
, 0, 0, sr
);
1677 cb_get_row (GnmStyle
*style
,
1678 int corner_col
, G_GNUC_UNUSED
int corner_row
,
1679 int width
, G_GNUC_UNUSED
int height
,
1680 GnmRange
const *apply_to
, gpointer user_
)
1682 GnmStyle
**res
= user_
;
1685 /* The given dimensions refer to the tile, not the area. */
1686 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
1688 for (i
= 0; i
< width
; i
++)
1689 res
[corner_col
+ i
] = style
;
1693 sheet_style_get_row2 (Sheet
const *sheet
, int row
)
1696 GnmStyle
**res
= g_new (GnmStyle
*, gnm_sheet_get_max_cols (sheet
));
1698 range_init_rows (&r
, sheet
, row
, row
);
1700 foreach_tile (sheet
, &r
, cb_get_row
, res
);
1709 * A small utility routine to initialize the grid drawing GnmStyleRow data
1713 style_row_init (GnmBorder
const * * *prev_vert
,
1714 GnmStyleRow
*sr
, GnmStyleRow
*next_sr
,
1715 int start_col
, int end_col
, gpointer mem
, gboolean hide_grid
)
1718 GnmBorder
const *none
= hide_grid
? NULL
: gnm_style_border_none ();
1720 /* alias the arrays for easy access so that array[col] is valid
1721 * for all elements start_col-1 .. end_col+1 inclusive.
1722 * Note that this means that in some cases array[-1] is legal.
1724 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
1726 sr
->vertical
-= start_col
-1;
1727 sr
->top
= sr
->vertical
+ n
;
1728 sr
->bottom
= sr
->top
+ n
;
1729 next_sr
->top
= sr
->bottom
; /* yes they should share */
1730 next_sr
->bottom
= next_sr
->top
+ n
;
1731 next_sr
->vertical
= next_sr
->bottom
+ n
;
1732 *prev_vert
= next_sr
->vertical
+ n
;
1733 sr
->styles
= ((GnmStyle
const **) (*prev_vert
+ n
));
1734 next_sr
->styles
= sr
->styles
+ n
;
1735 sr
->start_col
= next_sr
->start_col
= start_col
;
1736 sr
->end_col
= next_sr
->end_col
= end_col
;
1737 sr
->hide_grid
= next_sr
->hide_grid
= hide_grid
;
1739 /* Init the areas that sheet_style_get_row will not */
1740 for (col
= start_col
-1 ; col
<= end_col
+1; ++col
)
1741 (*prev_vert
)[col
] = sr
->top
[col
] = none
;
1742 sr
->vertical
[start_col
-1] = sr
->vertical
[end_col
+1] =
1743 next_sr
->vertical
[start_col
-1] = next_sr
->vertical
[end_col
+1] =
1744 next_sr
->top
[start_col
-1] = next_sr
->top
[end_col
+1] =
1745 next_sr
->bottom
[start_col
-1] = next_sr
->bottom
[end_col
+1] = none
;
1749 * sheet_style_apply_range:
1754 * Apply a partial style to a region.
1755 * The routine absorbs a reference to the partial style.
1758 sheet_style_apply_range (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1760 ReplacementStyle rs
;
1763 g_return_if_fail (IS_SHEET (sheet
));
1764 g_return_if_fail (range
!= NULL
);
1766 if (range
->start
.col
> range
->end
.col
||
1767 range
->start
.row
> range
->end
.row
) {
1768 gnm_style_unref (pstyle
);
1773 range_ensure_sanity (&r
, sheet
);
1775 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1776 cell_tile_apply (&sheet
->style_data
->styles
,
1777 sheet
->tile_top_level
, 0, 0,
1783 apply_border (Sheet
*sheet
, GnmRange
const *r
,
1784 GnmStyleBorderLocation side
,
1787 GnmStyle
*pstyle
= gnm_style_new ();
1788 pstyle_set_border (pstyle
, border
, side
);
1789 sheet_style_apply_range (sheet
, r
, pstyle
);
1793 * sheet_style_apply_border:
1798 * When a user applies a border to a region we attempt to remove the border
1799 * from the opposing side to avoid overlapping border specifications.
1801 * if we apply a top border to a range, we would clear the bottom border
1802 * of the range offset upwards.
1805 sheet_style_apply_border (Sheet
*sheet
,
1806 GnmRange
const *range
,
1807 GnmBorder
**borders
)
1809 GnmStyle
*pstyle
= NULL
;
1811 if (borders
== NULL
)
1814 if (borders
[GNM_STYLE_BORDER_TOP
]) {
1816 GnmRange r
= *range
;
1817 r
.end
.row
= r
.start
.row
;
1818 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1819 borders
[GNM_STYLE_BORDER_TOP
]);
1823 if (r
.start
.row
>= 0) {
1824 r
.end
.row
= r
.start
.row
;
1825 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1826 gnm_style_border_none ());
1830 if (borders
[GNM_STYLE_BORDER_BOTTOM
]) {
1831 /* 2.1 bottom inner */
1832 GnmRange r
= *range
;
1833 r
.start
.row
= r
.end
.row
;
1834 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1835 borders
[GNM_STYLE_BORDER_BOTTOM
]);
1837 /* 2.2 bottom outer */
1839 if (r
.end
.row
< gnm_sheet_get_last_row (sheet
)) {
1840 r
.start
.row
= r
.end
.row
;
1841 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1842 gnm_style_border_none ());
1846 if (borders
[GNM_STYLE_BORDER_LEFT
]) {
1847 /* 3.1 left inner */
1848 GnmRange r
= *range
;
1849 r
.end
.col
= r
.start
.col
;
1850 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1851 borders
[GNM_STYLE_BORDER_LEFT
]);
1853 /* 3.2 left outer */
1855 if (r
.start
.col
>= 0) {
1856 r
.end
.col
= r
.start
.col
;
1857 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1858 gnm_style_border_none ());
1862 if (borders
[GNM_STYLE_BORDER_RIGHT
]) {
1863 /* 4.1 right inner */
1864 GnmRange r
= *range
;
1865 r
.start
.col
= r
.end
.col
;
1866 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1867 borders
[GNM_STYLE_BORDER_RIGHT
]);
1869 /* 4.2 right outer */
1871 if (r
.end
.col
< gnm_sheet_get_last_col (sheet
)) {
1872 r
.start
.col
= r
.end
.col
;
1873 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1874 gnm_style_border_none ());
1878 /* Interiors horizontal : prefer top */
1879 if (borders
[GNM_STYLE_BORDER_HORIZ
] != NULL
) {
1880 /* 5.1 horizontal interior top */
1881 if (range
->start
.row
!= range
->end
.row
) {
1882 GnmRange r
= *range
;
1884 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1885 borders
[GNM_STYLE_BORDER_HORIZ
]);
1887 /* 5.2 interior bottom */
1888 if (range
->start
.row
!= range
->end
.row
) {
1889 GnmRange r
= *range
;
1891 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1892 gnm_style_border_none ());
1896 /* Interiors vertical: prefer left */
1897 if (borders
[GNM_STYLE_BORDER_VERT
] != NULL
) {
1898 /* 6.1 vertical interior left */
1899 if (range
->start
.col
!= range
->end
.col
) {
1900 GnmRange r
= *range
;
1902 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1903 borders
[GNM_STYLE_BORDER_VERT
]);
1906 /* 6.2 The vertical interior right */
1907 if (range
->start
.col
!= range
->end
.col
) {
1908 GnmRange r
= *range
;
1910 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1911 gnm_style_border_none ());
1915 /* 7. Diagonals (apply both in one pass) */
1916 if (borders
[GNM_STYLE_BORDER_DIAG
] != NULL
) {
1917 pstyle
= gnm_style_new ();
1918 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_DIAG
],
1919 GNM_STYLE_BORDER_DIAG
);
1921 if (borders
[GNM_STYLE_BORDER_REV_DIAG
]) {
1923 pstyle
= gnm_style_new ();
1924 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_REV_DIAG
],
1925 GNM_STYLE_BORDER_REV_DIAG
);
1928 sheet_style_apply_range (sheet
, range
, pstyle
);
1931 /****************************************************************************/
1935 unsigned int conflicts
;
1939 cb_find_conflicts (GnmStyle
*style
,
1940 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
1941 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
1942 G_GNUC_UNUSED GnmRange
const *apply_to
, FindConflicts
*ptr
)
1944 ptr
->conflicts
= gnm_style_find_conflicts (ptr
->accum
, style
, ptr
->conflicts
);
1948 border_mask_internal (gboolean
*known
, GnmBorder
**borders
,
1949 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1953 borders
[l
] = (GnmBorder
*)b
;
1954 gnm_style_border_ref (borders
[l
]);
1955 } else if (borders
[l
] != b
&& borders
[l
] != NULL
) {
1956 gnm_style_border_unref (borders
[l
]);
1962 border_mask (gboolean
*known
, GnmBorder
**borders
,
1963 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1966 b
= gnm_style_border_none ();
1967 border_mask_internal (known
, borders
, b
, l
);
1971 border_mask_vec (gboolean
*known
, GnmBorder
**borders
,
1972 GnmBorder
const * const *vec
, int first
, int last
,
1973 GnmStyleBorderLocation l
)
1975 GnmBorder
const *b
= vec
[first
];
1978 b
= gnm_style_border_none ();
1979 while (first
++ < last
) {
1980 GnmBorder
const *tmp
= vec
[first
];
1982 tmp
= gnm_style_border_none ();
1989 border_mask_internal (known
, borders
, b
, l
);
1993 * sheet_style_get_uniform:
1998 * Find out what style elements are common to every cell in a range
1999 * Returns a flag of TRUE if there was a conflict a given style element
2002 sheet_style_find_conflicts (Sheet
const *sheet
, GnmRange
const *r
,
2003 GnmStyle
**style
, GnmBorder
**borders
)
2005 int n
, col
, row
, start_col
, end_col
;
2007 gpointer
*sr_array_data
;
2008 GnmStyleBorderLocation i
;
2009 gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
];
2010 GnmBorder
const *none
= gnm_style_border_none ();
2013 g_return_val_if_fail (IS_SHEET (sheet
), 0);
2014 g_return_val_if_fail (r
!= NULL
, 0);
2015 g_return_val_if_fail (style
!= NULL
, 0);
2016 g_return_val_if_fail (borders
!= NULL
, 0);
2018 /* init style set with a copy of the top left corner of the 1st range */
2019 if (*style
== NULL
) {
2020 GnmStyle
const *tmp
= sheet_style_get (sheet
, r
->start
.col
, r
->start
.row
);
2021 *style
= gnm_style_dup (tmp
);
2022 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2024 borders
[i
] = gnm_style_border_ref ((GnmBorder
*)none
);
2027 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++)
2031 user
.accum
= *style
;
2032 user
.conflicts
= 0; /* no conflicts yet */
2033 foreach_tile (sheet
, r
, (ForeachTileFunc
)cb_find_conflicts
, &user
);
2035 /* copy over the diagonals */
2036 for (i
= GNM_STYLE_BORDER_REV_DIAG
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2037 GnmStyleElement se
= GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (i
);
2038 if (user
.conflicts
& (1 << se
))
2041 borders
[i
] = gnm_style_border_ref (
2042 gnm_style_get_border (*style
, se
));
2045 start_col
= r
->start
.col
;
2046 if (r
->start
.col
> 0)
2048 end_col
= r
->end
.col
;
2049 if (r
->end
.col
< gnm_sheet_get_max_cols (sheet
))
2052 /* allocate then alias the arrays for easy access */
2053 n
= end_col
- start_col
+ 2;
2054 g_assert (sizeof (GnmBorder
*) == sizeof (gpointer
));
2055 g_assert (sizeof (GnmStyle
*) == sizeof (gpointer
));
2056 sr_array_data
= g_new (gpointer
, n
* 4);
2057 sr
.vertical
= (GnmBorder
const **)(sr_array_data
- start_col
);
2058 sr
.top
= (GnmBorder
const **)(sr_array_data
+ n
- start_col
);
2059 sr
.bottom
= (GnmBorder
const **)(sr_array_data
+ 2 * n
- start_col
);
2060 sr
.styles
= (GnmStyle
const **) (sr_array_data
+ 3 * n
- start_col
);
2061 sr
.start_col
= start_col
;
2062 sr
.end_col
= end_col
;
2063 sr
.hide_grid
= sheet
->hide_grid
;
2065 /* pretend the previous bottom had no borders */
2066 for (col
= start_col
; col
<= end_col
; ++col
)
2069 /* merge the bottom of the previous row */
2070 if (r
->start
.row
> 0) {
2071 GnmBorder
const ** roller
;
2072 sr
.row
= r
->start
.row
- 1;
2073 sheet_style_get_row (sheet
, &sr
);
2074 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2078 * TODO: The border handling is tricky and currently VERY slow for
2079 * large ranges. We could easily optimize this. There is no need to
2080 * retrieve the style in every cell just to do a filter for uniformity
2081 * by row. One day we should do a special case version of
2082 * sheet_style_get_row probably style_get_uniform_col (this will be
2085 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
2086 GnmBorder
const **roller
;
2088 sheet_style_get_row (sheet
, &sr
);
2090 border_mask (known
, borders
, sr
.vertical
[r
->start
.col
],
2091 GNM_STYLE_BORDER_LEFT
);
2092 border_mask (known
, borders
, sr
.vertical
[r
->end
.col
+1],
2093 GNM_STYLE_BORDER_RIGHT
);
2094 border_mask_vec (known
, borders
, sr
.top
,
2095 r
->start
.col
, r
->end
.col
, (row
== r
->start
.row
)
2096 ? GNM_STYLE_BORDER_TOP
: GNM_STYLE_BORDER_HORIZ
);
2097 if (r
->start
.col
!= r
->end
.col
)
2098 border_mask_vec (known
, borders
, sr
.vertical
,
2099 r
->start
.col
+1, r
->end
.col
,
2100 GNM_STYLE_BORDER_VERT
);
2102 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2105 /* merge the top of the next row */
2106 if (r
->end
.row
< gnm_sheet_get_last_row (sheet
)) {
2107 sr
.row
= r
->end
.row
+ 1;
2108 sheet_style_get_row (sheet
, &sr
);
2110 border_mask_vec (known
, borders
, sr
.top
, r
->start
.col
, r
->end
.col
,
2111 GNM_STYLE_BORDER_BOTTOM
);
2113 g_free (sr_array_data
);
2114 return user
.conflicts
;
2118 * sheet_style_relocate:
2121 * Slide the styles from the origin region to the new position.
2124 sheet_style_relocate (GnmExprRelocateInfo
const *rinfo
)
2127 GnmStyleList
*styles
;
2129 g_return_if_fail (rinfo
!= NULL
);
2131 styles
= sheet_style_get_range (rinfo
->origin_sheet
, &rinfo
->origin
);
2133 sheet_style_set_range (rinfo
->origin_sheet
, &rinfo
->origin
,
2134 sheet_style_default (rinfo
->origin_sheet
));
2135 corner
.col
= rinfo
->origin
.start
.col
+ rinfo
->col_offset
;
2136 corner
.row
= rinfo
->origin
.start
.row
+ rinfo
->row_offset
;
2137 sheet_style_set_list (rinfo
->target_sheet
, &corner
, styles
, NULL
, NULL
);
2138 style_list_free (styles
);
2142 * sheet_style_insdel_colrow:
2145 * Insert of delete style columns/rows.
2147 * For the insert case, we stretch the preceding column/row into there space
2151 sheet_style_insdel_colrow (GnmExprRelocateInfo
const *rinfo
)
2153 GnmStyleList
*styles
= NULL
;
2158 g_return_if_fail (rinfo
!= NULL
);
2159 g_return_if_fail (rinfo
->origin_sheet
== rinfo
->target_sheet
);
2160 g_return_if_fail ((rinfo
->col_offset
== 0) != (rinfo
->row_offset
== 0));
2162 is_insert
= (rinfo
->col_offset
+ rinfo
->row_offset
> 0);
2163 sheet
= rinfo
->origin_sheet
;
2166 /* 1) copy col/row to the top/left of the region, and extend it */
2167 corner
= rinfo
->origin
.start
;
2168 if (rinfo
->col_offset
) {
2169 int col
= MAX (corner
.col
- 1, 0);
2174 range_init_cols (&r
, sheet
, col
, col
);
2175 styles
= sheet_style_get_range (sheet
, &r
);
2176 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2177 GnmStyleRegion
*sr
= ptr
->data
;
2178 sr
->range
.end
.col
= rinfo
->col_offset
- 1;
2181 int row
= MAX (corner
.row
- 1, 0);
2186 range_init_rows (&r
, sheet
, row
, row
);
2187 styles
= sheet_style_get_range (sheet
, &r
);
2188 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2189 GnmStyleRegion
*sr
= ptr
->data
;
2190 sr
->range
.end
.row
= rinfo
->row_offset
- 1;
2195 sheet_style_relocate (rinfo
);
2198 sheet_style_set_list (sheet
, &corner
, styles
, NULL
, NULL
);
2199 style_list_free (styles
);
2204 cb_style_extent (GnmStyle
*style
,
2205 int corner_col
, int corner_row
, int width
, int height
,
2206 GnmRange
const *apply_to
, gpointer user
)
2208 GnmRange
*res
= user
;
2209 if (gnm_style_visible_in_blank (style
)) {
2212 /* The given dimensions refer to the tile, not the area. */
2213 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2214 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2216 tmp
= corner_col
+width
-1;
2217 if (res
->end
.col
< tmp
)
2219 if (res
->start
.col
> corner_col
)
2220 res
->start
.col
= corner_col
;
2222 tmp
= corner_row
+height
-1;
2223 if (res
->end
.row
< tmp
)
2225 if (res
->start
.row
> corner_row
)
2226 res
->start
.row
= corner_row
;
2231 * sheet_style_get_extent:
2232 * @sheet: sheet to measure
2233 * @r: starting range and resulting range
2235 * A simple implementation that finds the smallest range containing all visible styles
2236 * and containing @res.
2239 sheet_style_get_extent (Sheet
const *sheet
, GnmRange
*res
)
2243 range_init_full_sheet (&r
, sheet
);
2244 foreach_tile (sheet
, &r
, cb_style_extent
, res
);
2247 struct cb_nondefault_extent
{
2249 GnmStyle
**col_defaults
;
2253 cb_nondefault_extent (GnmStyle
*style
,
2254 int corner_col
, int corner_row
, int width
, int height
,
2255 GnmRange
const *apply_to
, gpointer user_
)
2257 struct cb_nondefault_extent
*user
= user_
;
2258 GnmRange
*res
= user
->res
;
2261 for (i
= 0; i
< width
; i
++) {
2262 int col
= corner_col
+ i
;
2263 if (col
>= apply_to
->start
.col
&&
2264 col
<= apply_to
->end
.col
&&
2265 style
!= user
->col_defaults
[col
]) {
2266 int max_row
= MIN (corner_row
+ height
- 1,
2268 int min_row
= MAX (corner_row
, apply_to
->start
.row
);
2270 res
->start
.col
= MIN (col
, res
->start
.col
);
2271 res
->start
.row
= MIN (min_row
, res
->start
.row
);
2273 res
->end
.col
= MAX (col
, res
->end
.col
);
2274 res
->end
.row
= MAX (max_row
, res
->end
.row
);
2280 sheet_style_get_nondefault_extent (Sheet
const *sheet
, GnmRange
*extent
,
2281 const GnmRange
*src
, GnmStyle
**col_defaults
)
2283 struct cb_nondefault_extent user
;
2285 user
.col_defaults
= col_defaults
;
2286 foreach_tile (sheet
, src
, cb_nondefault_extent
, &user
);
2289 struct cb_is_default
{
2291 GnmStyle
**col_defaults
;
2295 cb_is_default (GnmStyle
*style
,
2296 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2297 int width
, G_GNUC_UNUSED
int height
,
2298 GnmRange
const *apply_to
, gpointer user_
)
2300 struct cb_is_default
*user
= user_
;
2303 /* The given "width" refers to the tile, not the area. */
2304 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2306 for (i
= 0; user
->res
&& i
< width
; i
++) {
2307 if (style
!= user
->col_defaults
[corner_col
+ i
])
2313 sheet_style_is_default (Sheet
const *sheet
, const GnmRange
*r
, GnmStyle
**col_defaults
)
2315 struct cb_is_default user
;
2318 user
.col_defaults
= col_defaults
;
2320 foreach_tile (sheet
, r
, cb_is_default
, &user
);
2325 struct cb_get_nondefault
{
2327 GnmStyle
**col_defaults
;
2331 cb_get_nondefault (GnmStyle
*style
,
2332 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2333 int width
, G_GNUC_UNUSED
int height
,
2334 GnmRange
const *apply_to
, gpointer user_
)
2336 struct cb_get_nondefault
*user
= user_
;
2339 /* The given dimensions refer to the tile, not the area. */
2340 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2341 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2343 for (i
= 0; i
< width
; i
++) {
2344 if (style
!= user
->col_defaults
[corner_col
+ i
]) {
2346 for (j
= 0; j
< height
; j
++)
2347 user
->res
[corner_row
+ j
] = 1;
2354 sheet_style_get_nondefault_rows (Sheet
const *sheet
, GnmStyle
**col_defaults
)
2356 struct cb_get_nondefault user
;
2359 range_init_full_sheet (&r
, sheet
);
2361 user
.res
= g_new0 (guint8
, gnm_sheet_get_max_rows (sheet
));
2362 user
.col_defaults
= col_defaults
;
2364 foreach_tile (sheet
, &r
, cb_get_nondefault
, &user
);
2369 struct cb_most_common
{
2376 cb_most_common (GnmStyle
*style
,
2377 int corner_col
, int corner_row
, int width
, int height
,
2378 GnmRange
const *apply_to
, gpointer user
)
2380 struct cb_most_common
*cmc
= user
;
2381 int *counts
= g_hash_table_lookup (cmc
->h
, style
);
2384 counts
= g_new0 (int, cmc
->l
);
2385 g_hash_table_insert (cmc
->h
, style
, counts
);
2388 /* The given dimensions refer to the tile, not the area. */
2389 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2390 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2393 for (i
= 0; i
< width
; i
++)
2394 counts
[corner_col
+ i
] += height
;
2396 for (i
= 0; i
< height
; i
++)
2397 counts
[corner_row
+ i
] += width
;
2401 * sheet_style_most_common:
2402 * @sheet: sheet to inspect
2403 * @is_col: if %TRUE, look for common styles in columns; if FALSE, look in rows.
2405 * Returns: an array of styles describing the most common styles, one per column
2409 sheet_style_most_common (Sheet
const *sheet
, gboolean is_col
)
2412 struct cb_most_common cmc
;
2415 GHashTableIter iter
;
2416 gpointer key
, value
;
2418 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2420 range_init_full_sheet (&r
, sheet
);
2421 cmc
.h
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, g_free
);
2422 cmc
.l
= colrow_max (is_col
, sheet
);
2423 cmc
.is_col
= is_col
;
2424 foreach_tile (sheet
, &r
, cb_most_common
, &cmc
);
2426 max
= g_new0 (int, cmc
.l
);
2427 res
= g_new0 (GnmStyle
*, cmc
.l
);
2428 g_hash_table_iter_init (&iter
, cmc
.h
);
2429 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
2430 int *counts
= value
;
2431 GnmStyle
*style
= key
;
2433 for (j
= 0; j
< cmc
.l
; j
++) {
2434 /* FIXME: we really ought to break ties in a
2435 consistent way that does not depend on hash
2437 if (counts
[j
] > max
[j
]) {
2443 g_hash_table_destroy (cmc
.h
);
2449 /****************************************************************************/
2452 * gnm_style_region_new:
2456 * Returns: (transfer full): the newly allocated #GnmStyleRegion.
2459 gnm_style_region_new (GnmRange
const *range
, GnmStyle
*style
)
2463 sr
= g_new (GnmStyleRegion
, 1);
2466 gnm_style_ref (style
);
2472 gnm_style_region_free (GnmStyleRegion
*sr
)
2474 g_return_if_fail (sr
!= NULL
);
2476 gnm_style_unref (sr
->style
);
2481 static GnmStyleRegion
*
2482 gnm_style_region_copy (GnmStyleRegion
*sr
)
2484 GnmStyleRegion
*res
= g_new (GnmStyleRegion
, 1);
2486 gnm_style_ref (sr
->style
);
2491 gnm_style_region_get_type (void)
2496 t
= g_boxed_type_register_static ("GnmStyleRegion",
2497 (GBoxedCopyFunc
)gnm_style_region_copy
,
2498 (GBoxedFreeFunc
)gnm_style_region_free
);
2505 debug_style_list (void)
2507 static int debug
= -1;
2509 debug
= gnm_debug_flag ("style-list");
2515 GHashTable
*by_tl
, *by_br
;
2517 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
);
2518 gboolean (*style_filter
) (GnmStyle
const *style
);
2519 GnmSheetSize
const *sheet_size
;
2523 merge_ranges (GnmRange
*a
, GnmRange
const *b
)
2525 if (a
->start
.row
== b
->start
.row
&&
2526 a
->end
.row
== b
->end
.row
&&
2527 a
->end
.col
+ 1 == b
->start
.col
) {
2528 /* "a" is just left of "b". */
2529 a
->end
.col
= b
->end
.col
;
2533 if (a
->start
.col
== b
->start
.col
&&
2534 a
->end
.col
== b
->end
.col
&&
2535 a
->end
.row
+ 1 == b
->start
.row
) {
2536 /* "a" is just on top of "b". */
2537 a
->end
.row
= b
->end
.row
;
2546 try_merge_pair (ISL
*data
, unsigned ui1
, unsigned ui2
)
2551 if (ui1
>= data
->accum
->len
|| ui2
>= data
->accum
->len
)
2554 a
= g_ptr_array_index (data
->accum
, ui1
);
2555 b
= g_ptr_array_index (data
->accum
, ui2
);
2557 if (!data
->style_equal (a
->style
, b
->style
))
2560 if (!merge_ranges (&a
->range
, &b
->range
))
2563 gnm_style_region_free (b
);
2564 g_ptr_array_remove_index (data
->accum
, ui2
);
2570 cb_style_list_add_node (GnmStyle
*style
,
2571 int corner_col
, int corner_row
, int width
, int height
,
2572 GnmRange
const *apply_to
, gpointer user_
)
2575 GnmSheetSize
const *ss
= data
->sheet_size
;
2579 /* Can this even happen? */
2580 if (corner_col
>= ss
->max_cols
|| corner_row
>= ss
->max_rows
)
2583 if (data
->style_filter
&& !data
->style_filter (style
))
2586 range
.start
.col
= corner_col
;
2587 range
.start
.row
= corner_row
;
2588 range
.end
.col
= MIN (corner_col
+ width
- 1, ss
->max_cols
- 1);
2589 range
.end
.row
= MIN (corner_row
+ height
- 1, ss
->max_rows
- 1);
2592 range
.start
.col
-= apply_to
->start
.col
;
2593 if (range
.start
.col
< 0)
2594 range
.start
.col
= 0;
2595 range
.start
.row
-= apply_to
->start
.row
;
2596 if (range
.start
.row
< 0)
2597 range
.start
.row
= 0;
2599 if (range
.end
.col
> apply_to
->end
.col
)
2600 range
.end
.col
= apply_to
->end
.col
;
2601 range
.end
.col
-= apply_to
->start
.col
;
2602 if (range
.end
.row
> apply_to
->end
.row
)
2603 range
.end
.row
= apply_to
->end
.row
;
2604 range
.end
.row
-= apply_to
->start
.row
;
2607 data
->area
+= (guint64
)range_width (&range
) * range_height (&range
);
2609 sr
= gnm_style_region_new (&range
, style
);
2610 g_ptr_array_add (data
->accum
, sr
);
2612 while (try_merge_pair (data
, data
->accum
->len
- 2, data
->accum
->len
- 1))
2617 verify_hashes (ISL
*data
)
2619 GHashTable
*by_tl
= data
->by_tl
;
2620 GHashTable
*by_br
= data
->by_br
;
2624 g_return_if_fail (g_hash_table_size (by_tl
) == data
->accum
->len
);
2625 g_return_if_fail (g_hash_table_size (by_br
) == data
->accum
->len
);
2627 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2628 GnmStyleRegion
*sr
= g_ptr_array_index (data
->accum
, ui
);
2629 g_return_if_fail (g_hash_table_lookup (by_tl
, &sr
->range
.start
) == sr
);
2630 g_return_if_fail (g_hash_table_lookup (by_br
, &sr
->range
.end
) == sr
);
2631 area
+= range_height (&sr
->range
) *
2632 (guint64
)range_width (&sr
->range
);
2635 g_return_if_fail (area
== data
->area
);
2639 merge_vertical_stripes (ISL
*data
)
2642 GHashTable
*by_tl
= data
->by_tl
;
2643 GHashTable
*by_br
= data
->by_br
;
2644 gboolean debug
= debug_style_list ();
2645 gboolean paranoid
= debug
;
2647 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2648 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2651 GSList
*Bs
= NULL
, *l
;
2652 gboolean fail
= FALSE
;
2654 /* We're looking for the setup below and extend Bs down */
2655 /* taking over part of C which is then extended to */
2656 /* include all of A. */
2660 /* +---------+ B1 | B2 | */
2662 /* +---------+----+---------+ */
2664 /* +------------------------+ */
2666 cr
.col
= a
->range
.start
.col
;
2667 cr
.row
= a
->range
.end
.row
+ 1;
2668 c
= g_hash_table_lookup (by_tl
, &cr
);
2669 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2672 cr
.col
= c
->range
.end
.col
;
2673 cr
.row
= a
->range
.end
.row
;
2674 while (cr
.col
> a
->range
.end
.col
) {
2675 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2676 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2680 Bs
= g_slist_prepend (Bs
, b
);
2681 cr
.col
= b
->range
.start
.col
- 1;
2683 if (fail
|| cr
.col
!= a
->range
.end
.col
) {
2689 g_printerr ("Vertical stripe merge:\n");
2690 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2691 for (l
= Bs
; l
; l
= l
-> next
) {
2692 GnmStyleRegion
*b
= l
->data
;
2693 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2695 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2698 g_hash_table_remove (by_tl
, &a
->range
.start
);
2699 g_hash_table_remove (by_br
, &a
->range
.end
);
2700 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2703 g_hash_table_remove (by_tl
, &c
->range
.start
);
2704 g_hash_table_remove (by_br
, &c
->range
.end
);
2705 c
->range
.start
.row
= a
->range
.start
.row
;
2706 c
->range
.end
.col
= a
->range
.end
.col
;
2707 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2708 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2710 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2712 for (l
= Bs
; l
; l
= l
-> next
) {
2713 GnmStyleRegion
*b
= l
->data
;
2714 g_hash_table_remove (by_br
, &b
->range
.end
);
2715 b
->range
.end
.row
= c
->range
.end
.row
;
2716 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2718 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2723 gnm_style_region_free (a
);
2726 if (paranoid
) verify_hashes (data
);
2731 merge_horizontal_stripes (ISL
*data
)
2734 GHashTable
*by_tl
= data
->by_tl
;
2735 GHashTable
*by_br
= data
->by_br
;
2736 gboolean debug
= debug_style_list ();
2737 gboolean paranoid
= debug
;
2739 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2740 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2743 GSList
*Bs
= NULL
, *l
;
2744 gboolean fail
= FALSE
;
2746 /* We're looking for the setup below and extend Bs right */
2747 /* taking over part of C which is then extended to */
2748 /* include all of A. */
2752 /* +----+-----+ | */
2754 /* +--+-------+ | */
2760 /* +-------+-----+ */
2762 cr
.col
= a
->range
.end
.col
+ 1;
2763 cr
.row
= a
->range
.start
.row
;
2764 c
= g_hash_table_lookup (by_tl
, &cr
);
2765 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2768 cr
.col
= a
->range
.end
.col
;
2769 cr
.row
= c
->range
.end
.row
;
2770 while (cr
.row
> a
->range
.end
.row
) {
2771 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2772 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2776 Bs
= g_slist_prepend (Bs
, b
);
2777 cr
.row
= b
->range
.start
.row
- 1;
2779 if (fail
|| cr
.row
!= a
->range
.end
.row
) {
2785 g_printerr ("Horizontal stripe merge:\n");
2786 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2787 for (l
= Bs
; l
; l
= l
-> next
) {
2788 GnmStyleRegion
*b
= l
->data
;
2789 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2791 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2794 g_hash_table_remove (by_tl
, &a
->range
.start
);
2795 g_hash_table_remove (by_br
, &a
->range
.end
);
2796 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2799 g_hash_table_remove (by_tl
, &c
->range
.start
);
2800 g_hash_table_remove (by_br
, &c
->range
.end
);
2801 c
->range
.start
.col
= a
->range
.start
.col
;
2802 c
->range
.end
.row
= a
->range
.end
.row
;
2803 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2804 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2806 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2808 for (l
= Bs
; l
; l
= l
-> next
) {
2809 GnmStyleRegion
*b
= l
->data
;
2810 g_hash_table_remove (by_br
, &b
->range
.end
);
2811 b
->range
.end
.col
= c
->range
.end
.col
;
2812 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2814 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2819 gnm_style_region_free (a
);
2822 if (paranoid
) verify_hashes (data
);
2827 by_col_row (GnmStyleRegion
**a
, GnmStyleRegion
**b
)
2831 d
= (*a
)->range
.start
.col
- (*b
)->range
.start
.col
;
2835 d
= (*a
)->range
.start
.row
- (*b
)->range
.start
.row
;
2839 static GnmStyleList
*
2840 internal_style_list (Sheet
const *sheet
, GnmRange
const *r
,
2841 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
),
2842 gboolean (*style_filter
) (GnmStyle
const *style
))
2844 GnmRange full_sheet
;
2846 GnmStyleList
*res
= NULL
;
2847 unsigned ui
, prelen
;
2848 gboolean paranoid
= FALSE
;
2851 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2854 /* This can happen if the last row or column is deleted. */
2855 if (!range_valid (r
))
2858 r
= range_init_full_sheet (&full_sheet
, sheet
);
2860 data
.accum
= g_ptr_array_new ();
2861 data
.by_tl
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2862 (GEqualFunc
)gnm_cellpos_equal
);
2863 data
.by_br
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2864 (GEqualFunc
)gnm_cellpos_equal
);
2866 data
.style_equal
= style_equal
;
2867 data
.style_filter
= style_filter
;
2868 data
.sheet_size
= gnm_sheet_get_size (sheet
);
2870 foreach_tile (sheet
, r
, cb_style_list_add_node
, &data
);
2872 sheet_area
= (guint64
)range_height (r
) * range_width (r
);
2873 if (data
.style_filter
? (data
.area
> sheet_area
) : (data
.area
!= sheet_area
))
2874 g_warning ("Strange size issue in internal_style_list");
2877 * Simple, fast optimization first. For the file underlying
2878 * bug 699045 this brings down 332688 entries to just 86.
2880 if (data
.accum
->len
>= 2) {
2881 g_ptr_array_sort (data
.accum
, (GCompareFunc
)by_col_row
);
2882 for (ui
= data
.accum
->len
- 1; ui
> 0; ui
--) {
2883 try_merge_pair (&data
, ui
- 1, ui
);
2887 /* Populate hashes. */
2888 for (ui
= 0; ui
< data
.accum
->len
; ui
++) {
2889 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2890 g_hash_table_insert (data
.by_tl
, &sr
->range
.start
, sr
);
2891 g_hash_table_insert (data
.by_br
, &sr
->range
.end
, sr
);
2894 if (paranoid
) verify_hashes (&data
);
2897 prelen
= data
.accum
->len
;
2898 merge_vertical_stripes (&data
);
2899 merge_horizontal_stripes (&data
);
2900 } while (prelen
> data
.accum
->len
);
2902 /* Always verify once. */
2903 verify_hashes (&data
);
2905 if (debug_style_list ())
2906 g_printerr ("Total of %d ranges:\n", data
.accum
->len
);
2907 for (ui
= data
.accum
->len
; ui
-- > 0; ) {
2908 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2909 if (debug_style_list ())
2910 g_printerr (" %s %p\n",
2911 range_as_string (&sr
->range
),
2913 res
= g_slist_prepend (res
, sr
);
2916 g_ptr_array_free (data
.accum
, TRUE
);
2917 g_hash_table_destroy (data
.by_tl
);
2918 g_hash_table_destroy (data
.by_br
);
2923 * sheet_style_get_range:
2924 * @sheet: the sheet in which to find styles
2925 * @r: optional range to scan
2927 * Get a list of rectangles and their associated styles.
2928 * Caller is responsible for freeing. Note that when a range is given,
2929 * the resulting ranges are relative to the input range.
2931 * Returns: (transfer full):
2934 sheet_style_get_range (Sheet
const *sheet
, GnmRange
const *r
)
2936 return internal_style_list (sheet
, r
,
2942 style_conditions_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2944 return gnm_style_get_conditions (a
) == gnm_style_get_conditions (b
);
2948 style_conditions_filter (GnmStyle
const *style
)
2950 return gnm_style_get_conditions (style
) != NULL
;
2954 * sheet_style_collect_conditions:
2958 * Returns: (transfer full): a list of areas with conditionals, Caller is
2959 * responsible for freeing.
2962 sheet_style_collect_conditions (Sheet
const *sheet
, GnmRange
const *r
)
2964 return internal_style_list (sheet
, r
,
2965 style_conditions_equal
,
2966 style_conditions_filter
);
2971 style_hlink_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2973 return gnm_style_get_hlink (a
) == gnm_style_get_hlink (b
);
2977 style_hlink_filter (GnmStyle
const *style
)
2979 return gnm_style_get_hlink (style
) != NULL
;
2983 * sheet_style_collect_hlinks:
2987 * Returns: (transfer full): a list of areas with hyperlinks, Caller is
2988 * responsible for freeing.
2991 sheet_style_collect_hlinks (Sheet
const *sheet
, GnmRange
const *r
)
2993 return internal_style_list (sheet
, r
,
2995 style_hlink_filter
);
3000 style_validation_equal (GnmStyle
const *a
, GnmStyle
const *b
)
3002 return gnm_style_get_validation (a
) == gnm_style_get_validation (b
) &&
3003 gnm_style_get_input_msg (a
) == gnm_style_get_input_msg (b
);
3007 style_validation_filter (GnmStyle
const *style
)
3009 return (gnm_style_get_validation (style
) != NULL
||
3010 gnm_style_get_input_msg (style
) != NULL
);
3014 * sheet_style_collect_validations:
3015 * @sheet: the to trawl
3016 * @r: (allow-none): range to restrict to
3018 * Returns: (transfer full): a list of areas with validation or input
3022 sheet_style_collect_validations (Sheet
const *sheet
, GnmRange
const *r
)
3024 return internal_style_list (sheet
, r
,
3025 style_validation_equal
,
3026 style_validation_filter
);
3030 * sheet_style_set_list:
3032 * @corner: The top-left corner (in LTR mode)
3034 * @range_modify: (scope call):
3037 * Overwrites the styles of the ranges given by @corner with the content of
3038 * @list. Optionally transposing the ranges
3041 sheet_style_set_list (Sheet
*sheet
, GnmCellPos
const *corner
,
3042 GnmStyleList
const *list
,
3043 sheet_style_set_list_cb_t range_modify
,
3046 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_SIMPLE
;
3047 GnmStyleList
const *l
;
3049 g_return_val_if_fail (IS_SHEET (sheet
), spanflags
);
3051 /* Sluggish but simple implementation for now */
3052 for (l
= list
; l
; l
= l
->next
) {
3053 GnmStyleRegion
const *sr
= l
->data
;
3054 GnmRange r
= sr
->range
;
3056 range_translate (&r
, sheet
, +corner
->col
, +corner
->row
);
3058 range_modify (&r
, sheet
, data
);
3060 gnm_style_ref (sr
->style
);
3061 sheet_style_set_range (sheet
, &r
, sr
->style
);
3062 spanflags
|= gnm_style_required_spanflags (sr
->style
);
3069 * @l: the list to free
3071 * Free up the ressources in the style list. Including unreferencing the
3075 style_list_free (GnmStyleList
*list
)
3077 g_slist_free_full (list
, (GDestroyNotify
)gnm_style_region_free
);
3081 * style_list_get_style:
3086 * Attempts to find the style associated with the @pos offset within the 0,0
3088 * The resulting style does not have its reference count bumped.
3091 style_list_get_style (GnmStyleList
const *list
, int col
, int row
)
3093 GnmStyleList
const *l
;
3095 for (l
= list
; l
; l
= l
->next
) {
3096 GnmStyleRegion
const *sr
= l
->data
;
3097 GnmRange
const *r
= &sr
->range
;
3098 if (range_contains (r
, col
, row
))
3105 cb_find_link (GnmStyle
*style
,
3106 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
3107 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
3108 G_GNUC_UNUSED GnmRange
const *apply_to
, gpointer user
)
3110 GnmHLink
**plink
= user
;
3112 *plink
= gnm_style_get_hlink (style
);
3116 * sheet_style_region_contains_link:
3120 * Utility routine that checks to see if a region contains at least 1 hyper link
3121 * and returns the 1st one it finds.
3123 * Returns: (transfer none): the found #GmHLink if any.
3126 sheet_style_region_contains_link (Sheet
const *sheet
, GnmRange
const *r
)
3128 GnmHLink
*res
= NULL
;
3130 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3131 g_return_val_if_fail (r
!= NULL
, NULL
);
3133 foreach_tile (sheet
, r
, cb_find_link
, &res
);
3138 * sheet_style_foreach:
3140 * @func: (scope call): callback
3141 * @user_data: user data.
3143 * Executes @func for each style in the sheet.
3146 sheet_style_foreach (Sheet
const *sheet
, GFunc func
, gpointer user_data
)
3150 g_return_if_fail (IS_SHEET (sheet
));
3151 g_return_if_fail (sheet
->style_data
!= NULL
);
3153 styles
= sh_all_styles (sheet
->style_data
->style_hash
);
3154 styles
= g_slist_sort (styles
, (GCompareFunc
)gnm_style_cmp
);
3155 g_slist_foreach (styles
, func
, user_data
);
3156 g_slist_free (styles
);
3160 * sheet_style_range_foreach:
3162 * @r: optional range
3163 * @func: (scope call): callback.
3164 * @user_data: user data
3168 sheet_style_range_foreach (Sheet
const *sheet
, GnmRange
const *r
,
3169 GHFunc func
, gpointer user_data
)
3171 GnmStyleList
*styles
, *l
;
3173 styles
= sheet_style_get_range (sheet
, r
);
3175 for (l
= styles
; l
; l
= l
->next
) {
3176 GnmStyleRegion
*sr
= l
->data
;
3178 sr
->range
.start
.col
+= r
->start
.col
;
3179 sr
->range
.start
.row
+= r
->start
.row
;
3180 sr
->range
.end
.col
+= r
->start
.col
;
3181 sr
->range
.end
.row
+= r
->start
.row
;
3183 func (NULL
, sr
, user_data
);
3184 gnm_style_region_free (sr
);
3187 g_slist_free (styles
);
3190 /* ------------------------------------------------------------------------- */
3193 cell_tile_dump (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3197 int const w
= tile_widths
[level
];
3198 int const h
= tile_heights
[level
];
3200 const char *indent
= "";
3202 type
= (*tile
)->type
;
3206 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3207 data
->ss
->max_cols
- 1),
3208 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3209 data
->ss
->max_rows
- 1));
3211 g_printerr ("%s%s: type %s\n",
3213 range_as_string (&rng
),
3214 tile_type_str
[type
]);
3216 if (type
== TILE_PTR_MATRIX
) {
3219 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3220 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3221 int c
= i
% TILE_SIZE_COL
;
3222 int r
= i
/ TILE_SIZE_COL
;
3223 cell_tile_dump (subtile
, level
- 1, data
,
3231 for (i
= 0; i
< tile_size
[type
]; i
++) {
3232 g_printerr ("%s: %d %p\n",
3235 (*tile
)->style_any
.style
[i
]);
3241 /* ------------------------------------------------------------------------- */
3244 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3248 int const w
= tile_widths
[level
];
3249 int const h
= tile_heights
[level
];
3254 type
= (*tile
)->type
;
3255 if (type
== TILE_SIMPLE
)
3260 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3261 data
->ss
->max_cols
- 1),
3262 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3263 data
->ss
->max_rows
- 1));
3268 if (!tile_is_uniform (*tile
))
3274 case TILE_PTR_MATRIX
: {
3275 gboolean all_simple
= TRUE
;
3278 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3279 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3280 if (data
->recursion
) {
3281 int c
= i
% TILE_SIZE_COL
;
3282 int r
= i
/ TILE_SIZE_COL
;
3283 cell_tile_optimize (subtile
, level
- 1, data
,
3287 if ((*subtile
)->type
!= TILE_SIMPLE
)
3293 res
= cell_tile_style_new (NULL
, TILE_MATRIX
);
3294 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3295 CellTile
*subtile
= (*tile
)->ptr_matrix
.ptr
[i
];
3296 GnmStyle
*st
= subtile
->style_simple
.style
[0];
3297 gnm_style_link (st
);
3298 res
->style_matrix
.style
[i
] = st
;
3301 if (debug_style_optimize
)
3302 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3303 range_as_string (&rng
),
3304 range_width (&rng
), range_height (&rng
),
3305 tile_type_str
[(*tile
)->type
],
3306 tile_type_str
[res
->type
]);
3307 cell_tile_dtor (*tile
);
3314 gboolean csame
= TRUE
;
3315 gboolean rsame
= TRUE
;
3318 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
3319 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
3321 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3322 (*tile
)->style_matrix
.style
[i
])) {
3328 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3329 (*tile
)->style_matrix
.style
[ c
])) {
3348 g_assert_not_reached ();
3351 if (debug_style_optimize
)
3352 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3353 range_as_string (&rng
),
3354 range_width (&rng
), range_height (&rng
),
3355 tile_type_str
[(*tile
)->type
],
3356 tile_type_str
[type
]);
3358 res
= cell_tile_style_new (NULL
, type
);
3361 res
->style_simple
.style
[0] = (*tile
)->style_any
.style
[0];
3364 for (i
= 0; i
< TILE_SIZE_ROW
; i
++)
3365 res
->style_row
.style
[i
] =
3366 (*tile
)->style_matrix
.style
[i
* TILE_SIZE_COL
];
3369 for (i
= 0; i
< TILE_SIZE_COL
; i
++)
3370 res
->style_col
.style
[i
] =
3371 (*tile
)->style_matrix
.style
[i
];
3374 g_assert_not_reached ();
3377 for (i
= 0; i
< tile_size
[type
]; i
++)
3378 gnm_style_link (res
->style_any
.style
[i
]);
3380 cell_tile_dtor (*tile
);
3385 sample_styles (Sheet
*sheet
)
3387 GnmSheetSize
const *ss
= gnm_sheet_get_size (sheet
);
3393 GnmStyle
const *mstyle
= sheet_style_get (sheet
, c
, r
);
3394 if (res
== NULL
|| !gnm_style_eq (mstyle
, res
->data
)) {
3395 gnm_style_ref (mstyle
);
3396 res
= g_slist_prepend (res
, GINT_TO_POINTER (c
));
3397 res
= g_slist_prepend (res
, GINT_TO_POINTER (r
));
3398 res
= g_slist_prepend (res
, (gpointer
)mstyle
);
3402 if (c
>= ss
->max_cols
) {
3405 if (r
>= ss
->max_rows
)
3410 return g_slist_reverse (res
);
3414 verify_styles (GSList
*pre
, GSList
*post
)
3416 GSList
*lpre
, *lpost
;
3417 gboolean silent
= FALSE
, bad
= FALSE
;
3419 for (lpre
= pre
, lpost
= post
;
3421 lpre
= (lpre
? lpre
->next
->next
->next
: NULL
),
3422 lpost
= (lpost
? lpost
->next
->next
->next
: NULL
)) {
3423 int cpre
= lpre
? GPOINTER_TO_INT (lpre
->data
) : -1;
3424 int rpre
= lpre
? GPOINTER_TO_INT (lpre
->next
->data
) : -1;
3425 GnmStyle
const *spre
= lpre
? lpre
->next
->next
->data
: NULL
;
3426 int cpost
= lpost
? GPOINTER_TO_INT (lpost
->data
) : -1;
3427 int rpost
= lpost
? GPOINTER_TO_INT (lpost
->next
->data
) : -1;
3428 GnmStyle
const *spost
= lpost
? lpost
->next
->next
->data
: NULL
;
3431 if (!spre
|| !spost
) {
3433 g_warning ("Style optimizer failure at end!");
3435 } else if (cpre
!= cpost
|| rpre
!= rpost
) {
3437 g_warning ("Style optimizer position conflict at %s!",
3438 cell_coord_name (cpre
, rpre
));
3440 } else if (!gnm_style_eq (spre
, spost
)) {
3442 g_warning ("Style optimizer failure at %s!",
3443 cell_coord_name (cpre
, rpre
));
3447 if (spre
) gnm_style_unref (spre
);
3448 if (spost
) gnm_style_unref (spost
);
3452 g_slist_free (post
);
3458 sheet_style_optimize (Sheet
*sheet
)
3460 CellTileOptimize data
;
3464 g_return_if_fail (IS_SHEET (sheet
));
3466 if (gnm_debug_flag ("no-style-optimize"))
3469 sheet_colrow_optimize (sheet
);
3471 data
.ss
= gnm_sheet_get_size (sheet
);
3472 data
.recursion
= TRUE
;
3474 if (debug_style_optimize
) {
3475 g_printerr ("Optimizing %s\n", sheet
->name_unquoted
);
3476 cell_tile_dump (&sheet
->style_data
->styles
,
3477 sheet
->tile_top_level
, &data
,
3481 verify
= gnm_debug_flag ("style-optimize-verify");
3482 pre
= verify
? sample_styles (sheet
) : NULL
;
3484 cell_tile_optimize (&sheet
->style_data
->styles
,
3485 sheet
->tile_top_level
, &data
,
3488 if (debug_style_optimize
)
3489 g_printerr ("Optimizing %s...done\n", sheet
->name_unquoted
);
3492 GSList
*post
= sample_styles (sheet
);
3493 verify_styles (pre
, post
);