2 * sheet-style.c: storage mechanism for styles and eventually cells.
4 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
5 * Copyright 2013 Morten Welinder (terra@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) version 3.
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
23 #include <gnumeric-config.h>
24 #include <sheet-style.h>
29 #include <style-border.h>
30 #include <style-color.h>
31 #include <style-conditions.h>
32 #include <parse-util.h>
35 #include <goffice/goffice.h>
36 #include <glib/gi18n-lib.h>
40 #define USE_TILE_POOLS 0
42 /* ------------------------------------------------------------------------- */
45 * This is, essentially, an std::multiset implementation for the style hash.
46 * Note, however, that sh_lookup is based on gnm_style_equal, not gnm_style_eq.
48 typedef GHashTable GnmStyleHash
;
51 /* This is a really crummy hash -- except for forcing collisions. */
52 #define gnm_style_hash(st) 0
56 sh_remove (GnmStyleHash
*h
, GnmStyle
*st
)
58 guint32 hv
= gnm_style_hash (st
);
59 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
61 g_return_if_fail (l
!= NULL
);
64 GSList
*next
= l
->next
;
66 /* We're removing the first of several elements. */
68 g_hash_table_replace (h
, GUINT_TO_POINTER (hv
), next
);
70 /* We're removing the last element. */
71 g_hash_table_remove (h
, GUINT_TO_POINTER (hv
));
74 /* We're removing an element that isn't first. */
75 l
= g_slist_remove (l
, st
);
80 sh_lookup (GnmStyleHash
*h
, GnmStyle
*st
)
82 guint32 hv
= gnm_style_hash (st
);
83 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
85 GnmStyle
*st2
= l
->data
;
86 /* NOTE: This uses gnm_style_equal, not gnm_style_eq. */
87 if (gnm_style_equal (st
, st2
))
95 sh_insert (GnmStyleHash
*h
, GnmStyle
*st
)
97 GSList
*s
= g_slist_prepend (NULL
, st
);
98 guint32 hv
= gnm_style_hash (st
);
99 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
104 g_hash_table_insert (h
, GUINT_TO_POINTER (hv
), s
);
109 sh_all_styles (GnmStyleHash
*h
)
115 g_hash_table_iter_init (&iter
, h
);
116 while (g_hash_table_iter_next (&iter
, NULL
, &value
)) {
118 for (; l
; l
= l
->next
)
119 res
= g_slist_prepend (res
, l
->data
);
125 static GnmStyleHash
*
128 return g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
129 NULL
, (GDestroyNotify
)g_slist_free
);
133 sh_destroy (GnmStyleHash
*h
)
135 g_hash_table_destroy (h
);
138 /* ------------------------------------------------------------------------- */
140 typedef union _CellTile CellTile
;
141 struct _GnmSheetStyleData
{
143 * style_hash is a set of all styles used by this sheet. These
144 * styles are all linked.
146 * We always re-use styles from here when we can, but there can
147 * still be duplicates. This happens when styles are changed
148 * while they are in the hash. For example, this happens when
149 * an expression used by a validation style changes due to
150 * row/col insert/delete.
152 GnmStyleHash
*style_hash
;
155 GnmStyle
*default_style
;
156 GnmColor
*auto_pattern_color
;
159 static gboolean debug_style_optimize
;
162 GnmSheetSize
const *ss
;
167 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
173 * For internal use only
176 sheet_style_unlink (Sheet
*sheet
, GnmStyle
*st
)
178 if (sheet
->style_data
->style_hash
)
179 sh_remove (sheet
->style_data
->style_hash
, st
);
184 * @sheet: (transfer full): the sheet
185 * @st: (transfer full): a style
187 * Looks up a style from the sheets collection. Linking if necessary.
189 * Returns: (transfer full): the new style.
192 sheet_style_find (Sheet
const *sheet
, GnmStyle
*s
)
195 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
197 gnm_style_link (res
);
202 s
= gnm_style_link_sheet (s
, (Sheet
*)sheet
);
204 /* Retry the lookup in case "s" changed. See #585178. */
205 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
207 gnm_style_link (res
);
209 * We are abandoning the linking here. We cannot use
210 * gnm_style_unlink as that would call sheet_style_unlink
211 * and thus remove "res" from the hash.
213 gnm_style_abandon_link (s
);
219 sh_insert (sheet
->style_data
->style_hash
, s
);
223 /* Place holder until I merge in the new styles too */
225 pstyle_set_border (GnmStyle
*st
, GnmBorder
*border
,
226 GnmStyleBorderLocation side
)
228 gnm_style_set_border (st
,
229 GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (side
),
230 gnm_style_border_ref (border
));
233 /* Amortize the cost of applying a partial style over a large region
234 * by caching and rereferencing the merged result for repeated styles.
244 rstyle_ctor_style (ReplacementStyle
*res
, GnmStyle
*new_style
, Sheet
*sheet
)
247 res
->new_style
= sheet_style_find (sheet
, new_style
);
253 rstyle_ctor_pstyle (ReplacementStyle
*res
, GnmStyle
*pstyle
, Sheet
*sheet
)
256 res
->new_style
= NULL
;
257 res
->pstyle
= pstyle
;
258 res
->cache
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
262 cb_style_unlink (gpointer key
, gpointer value
, G_GNUC_UNUSED gpointer user_data
)
264 gnm_style_unlink ((GnmStyle
*)key
);
265 gnm_style_unlink ((GnmStyle
*)value
);
269 rstyle_dtor (ReplacementStyle
*rs
)
271 if (rs
->cache
!= NULL
) {
272 g_hash_table_foreach (rs
->cache
, cb_style_unlink
, NULL
);
273 g_hash_table_destroy (rs
->cache
);
276 if (rs
->new_style
!= NULL
) {
277 gnm_style_unlink (rs
->new_style
);
278 rs
->new_style
= NULL
;
280 if (rs
->pstyle
!= NULL
) {
281 gnm_style_unref (rs
->pstyle
);
287 * rstyle_apply: Utility routine that is at the core of applying partial
288 * styles or storing complete styles. It will eventually be smarter
289 * and will maintain the cache of styles associated with each sheet
292 rstyle_apply (GnmStyle
**old
, ReplacementStyle
*rs
, GnmRange
const *r
)
295 g_return_if_fail (old
!= NULL
);
296 g_return_if_fail (rs
!= NULL
);
298 if (rs
->pstyle
!= NULL
) {
299 /* Cache the merged styles keeping a reference to the originals
300 * just in case all instances change.
302 s
= g_hash_table_lookup (rs
->cache
, *old
);
304 GnmStyle
*tmp
= gnm_style_new_merged (*old
, rs
->pstyle
);
305 s
= sheet_style_find (rs
->sheet
, tmp
);
306 gnm_style_link (*old
);
307 g_hash_table_insert (rs
->cache
, *old
, s
);
314 gnm_style_unlink_dependents (*old
, r
);
315 gnm_style_unlink (*old
);
318 gnm_style_link_dependents (s
, r
);
326 sheet_style_clear_style_dependents (Sheet
*sheet
, GnmRange
const *r
)
328 GSList
*styles
= sh_all_styles (sheet
->style_data
->style_hash
);
329 g_slist_foreach (styles
,
330 (GFunc
)gnm_style_unlink_dependents
,
332 g_slist_free (styles
);
336 /****************************************************************************/
338 /* If you change this, change the tile_{widths,heights} here
339 * and GNM_MAX_COLS and GNM_MAX_ROWS in gnumeric.h */
340 #define TILE_TOP_LEVEL 6
342 #define TILE_SIZE_COL 8
343 #define TILE_SIZE_ROW 16
353 static int const tile_size
[/*type*/] = {
355 TILE_SIZE_COL
, /* TILE_COL */
356 TILE_SIZE_ROW
, /* TILE_ROW */
357 TILE_SIZE_COL
* TILE_SIZE_ROW
/* TILE_MATRIX */
359 static int const tile_col_count
[/*type*/] = {
361 TILE_SIZE_COL
, /* TILE_COL */
363 TILE_SIZE_COL
, /* TILE_MATRIX */
364 TILE_SIZE_COL
/* TILE_PTR_MATRIX */
366 static int const tile_row_count
[/*type*/] = {
369 TILE_SIZE_ROW
, /* TILE_ROW */
370 TILE_SIZE_ROW
, /* TILE_MATRIX */
371 TILE_SIZE_ROW
/* TILE_PTR_MATRIX */
373 static const char * const tile_type_str
[/*type*/] = {
374 "simple", "col", "row", "matrix", "ptr-matrix"
376 static int const tile_widths
[/*level*/] = {
379 TILE_SIZE_COL
* TILE_SIZE_COL
,
380 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
381 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
382 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
383 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
384 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
386 static int const tile_heights
[/*level*/] = {
389 TILE_SIZE_ROW
* TILE_SIZE_ROW
,
390 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
391 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
392 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
393 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
394 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
398 CellTileType
const type
;
400 } CellTileStyleSimple
;
402 CellTileType
const type
;
403 GnmStyle
*style
[TILE_SIZE_COL
];
406 CellTileType
const type
;
407 GnmStyle
*style
[TILE_SIZE_ROW
];
410 CellTileType
const type
;
411 GnmStyle
*style
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
412 } CellTileStyleMatrix
;
414 CellTileType
const type
;
415 CellTile
*ptr
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
419 CellTileType
const type
;
420 CellTileStyleSimple style_any
;
421 CellTileStyleSimple style_simple
;
422 CellTileStyleCol style_col
;
423 CellTileStyleRow style_row
;
424 CellTileStyleMatrix style_matrix
;
425 CellTilePtrMatrix ptr_matrix
;
428 static int active_sheet_count
;
430 static GOMemChunk
*tile_pools
[5];
431 #define CHUNK_ALLOC(T,ctt) ((T*)go_mem_chunk_alloc (tile_pools[(ctt)]))
432 #define CHUNK_FREE(ctt,v) go_mem_chunk_free (tile_pools[(ctt)], (v))
434 static const size_t tile_type_sizeof
[5] = {
435 sizeof (CellTileStyleSimple
),
436 sizeof (CellTileStyleCol
),
437 sizeof (CellTileStyleRow
),
438 sizeof (CellTileStyleMatrix
),
439 sizeof (CellTilePtrMatrix
)
441 static int tile_allocations
= 0;
443 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_slice_alloc (tile_type_sizeof[(ctt)]))
444 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_slice_free1 (tile_type_sizeof[(ctt)], (v)))
446 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_malloc (tile_type_sizeof[(ctt)]))
447 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_free ((v)))
453 * Destroy a CellTile (recursively if needed). This will unlink all the
454 * styles in it. We do _not_ unlink style dependents here. That is done
455 * only in rstyle_apply.
458 cell_tile_dtor (CellTile
*tile
)
462 g_return_if_fail (tile
!= NULL
);
465 if (t
== TILE_PTR_MATRIX
) {
466 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
468 cell_tile_dtor (tile
->ptr_matrix
.ptr
[i
]);
469 tile
->ptr_matrix
.ptr
[i
] = NULL
;
471 } else if (TILE_SIMPLE
<= t
&& t
<= TILE_MATRIX
) {
472 int i
= tile_size
[t
];
474 gnm_style_unlink (tile
->style_any
.style
[i
]);
475 tile
->style_any
.style
[i
] = NULL
;
478 g_return_if_fail (FALSE
); /* don't free anything */
481 *((CellTileType
*)&(tile
->type
)) = TILE_UNDEFINED
; /* poison it */
482 CHUNK_FREE (t
, tile
);
486 cell_tile_style_new (GnmStyle
*style
, CellTileType t
)
488 CellTile
*res
= CHUNK_ALLOC (CellTile
, t
);
489 *((CellTileType
*)&(res
->type
)) = t
;
492 int i
= tile_size
[t
];
493 gnm_style_link_multiple (style
, i
);
495 res
->style_any
.style
[i
] = style
;
502 cell_tile_ptr_matrix_new (CellTile
*t
)
504 CellTilePtrMatrix
*res
;
506 g_return_val_if_fail (t
!= NULL
, NULL
);
507 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
508 TILE_MATRIX
>= t
->type
, NULL
);
510 res
= CHUNK_ALLOC (CellTilePtrMatrix
, TILE_PTR_MATRIX
);
511 *((CellTileType
*)&(res
->type
)) = TILE_PTR_MATRIX
;
514 * If we wanted to get fancy we could use self similarity to decrease
515 * the number of subtiles. However, this would increase the cost of
516 * applying changes later so I'm not sure it is worth the effort.
520 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
522 res
->ptr
[i
] = cell_tile_style_new (
523 t
->style_simple
.style
[0], TILE_SIMPLE
);
528 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
529 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
530 res
->ptr
[i
++] = cell_tile_style_new (
531 t
->style_col
.style
[c
], TILE_SIMPLE
);
536 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
537 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
538 res
->ptr
[i
++] = cell_tile_style_new (
539 t
->style_row
.style
[r
], TILE_SIMPLE
);
543 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
545 res
->ptr
[i
] = cell_tile_style_new (
546 t
->style_matrix
.style
[i
], TILE_SIMPLE
);
552 return (CellTile
*)res
;
556 cell_tile_matrix_set (CellTile
*t
)
559 CellTileStyleMatrix
*res
;
561 g_return_val_if_fail (t
!= NULL
, NULL
);
562 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
563 TILE_MATRIX
>= t
->type
, NULL
);
565 if (t
->type
== TILE_MATRIX
)
568 res
= (CellTileStyleMatrix
*)cell_tile_style_new (NULL
, TILE_MATRIX
);
572 GnmStyle
*tmp
= t
->style_simple
.style
[0];
573 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
574 gnm_style_link_multiple (tmp
, i
);
582 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
)
583 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
584 gnm_style_link (res
->style
[i
++] =
585 t
->style_col
.style
[c
]);
591 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
) {
592 GnmStyle
*tmp
= t
->style_row
.style
[r
];
593 gnm_style_link_multiple (tmp
, TILE_SIZE_COL
);
594 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
595 res
->style
[i
++] = tmp
;
602 g_assert_not_reached();
607 return (CellTile
*)res
;
610 /****************************************************************************/
613 sheet_style_sanity_check (void)
618 for (c
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
619 g_assert (c
< G_MAXUINT
/ TILE_SIZE_COL
);
622 g_assert (c
>= GNM_MAX_COLS
);
624 for (r
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
625 g_assert (r
< G_MAXUINT
/ TILE_SIZE_COL
);
628 g_assert (r
>= GNM_MAX_ROWS
);
630 g_assert (G_N_ELEMENTS (tile_heights
) > TILE_TOP_LEVEL
+ 1);
632 g_assert (G_N_ELEMENTS (tile_widths
) > TILE_TOP_LEVEL
+ 1);
636 sheet_style_init_size (Sheet
*sheet
, int cols
, int rows
)
638 GnmStyle
*default_style
;
639 int lc
= 0, lr
= 0, w
= TILE_SIZE_COL
, h
= TILE_SIZE_ROW
;
649 sheet
->tile_top_level
= MAX (lc
, lr
);
651 if (active_sheet_count
++ == 0) {
653 tile_pools
[TILE_SIMPLE
] =
654 go_mem_chunk_new ("simple tile pool",
655 sizeof (CellTileStyleSimple
),
657 tile_pools
[TILE_COL
] =
658 go_mem_chunk_new ("column tile pool",
659 sizeof (CellTileStyleCol
),
661 tile_pools
[TILE_ROW
] =
662 go_mem_chunk_new ("row tile pool",
663 sizeof (CellTileStyleRow
),
665 tile_pools
[TILE_MATRIX
] =
666 go_mem_chunk_new ("matrix tile pool",
667 sizeof (CellTileStyleMatrix
),
668 MAX (16 * 1024 - 128,
669 100 * sizeof (CellTileStyleMatrix
)));
671 /* If this fails one day, just make two pools. */
672 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
673 tile_pools
[TILE_PTR_MATRIX
] = tile_pools
[TILE_MATRIX
];
677 sheet
->style_data
= g_new (GnmSheetStyleData
, 1);
678 sheet
->style_data
->style_hash
= sh_create ();
680 sheet
->style_data
->auto_pattern_color
= style_color_auto_pattern ();
682 default_style
= gnm_style_new_default ();
684 /* We can not do this, XL creates full page charts with background
685 * 'none' by default. Then displays that as white. */
686 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
687 gnm_style_set_back_color (default_style
,
688 gnm_color_new_rgb8 (0x50, 0x50, 0x50));
689 gnm_style_set_pattern (default_style
, 1);
692 sheet
->style_data
->default_style
=
693 sheet_style_find (sheet
, default_style
);
694 sheet
->style_data
->styles
=
695 cell_tile_style_new (sheet
->style_data
->default_style
,
700 sheet_style_init (Sheet
*sheet
)
702 int cols
= gnm_sheet_get_max_cols (sheet
);
703 int rows
= gnm_sheet_get_max_rows (sheet
);
705 debug_style_optimize
= gnm_debug_flag ("style-optimize");
707 sheet_style_sanity_check ();
709 sheet_style_init_size (sheet
, cols
, rows
);
713 sheet_style_resize (Sheet
*sheet
, int cols
, int rows
)
715 GnmStyleList
*styles
, *l
;
716 int old_cols
= gnm_sheet_get_max_cols (sheet
);
717 int old_rows
= gnm_sheet_get_max_rows (sheet
);
718 GnmRange save_range
, new_full
;
720 /* Save the style for the surviving area. */
721 range_init (&save_range
, 0, 0,
722 MIN (cols
, old_cols
) - 1, MIN (rows
, old_rows
) - 1);
723 styles
= sheet_style_get_range (sheet
, &save_range
);
725 /* Build new empty structures. */
726 sheet_style_shutdown (sheet
);
727 sheet_style_init_size (sheet
, cols
, rows
);
729 /* Reapply styles. */
730 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
731 for (l
= styles
; l
; l
= l
->next
) {
732 GnmStyleRegion
const *sr
= l
->data
;
733 GnmRange
const *r
= &sr
->range
;
734 GnmStyle
*style
= sr
->style
;
736 if (range_intersection (&newr
, r
, &new_full
))
737 sheet_style_apply_range2 (sheet
, &newr
, style
);
740 style_list_free (styles
);
745 cb_tile_pool_leak (gpointer data
, gpointer user
)
747 CellTile
*tile
= data
;
748 g_printerr ("Leaking tile at %p.\n", (void *)tile
);
753 sheet_style_shutdown (Sheet
*sheet
)
758 g_return_if_fail (IS_SHEET (sheet
));
759 g_return_if_fail (sheet
->style_data
!= NULL
);
762 * Clear all styles. This is an easy way to clear out all
763 * style dependencies.
765 range_init_full_sheet (&r
, sheet
);
766 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
768 cell_tile_dtor (sheet
->style_data
->styles
);
769 sheet
->style_data
->styles
= NULL
;
771 sheet
->style_data
->default_style
= NULL
;
773 /* Clear the pointer to the hash BEFORE clearing and add a test in
774 * sheet_style_unlink. If we don't then it is possible/probable that
775 * unlinking the styles will attempt to remove them from the hash while
778 table
= sheet
->style_data
->style_hash
;
779 sheet
->style_data
->style_hash
= NULL
;
780 g_slist_free_full (sh_all_styles (table
),
781 (GDestroyNotify
)gnm_style_unlink
);
783 style_color_unref (sheet
->style_data
->auto_pattern_color
);
785 g_free (sheet
->style_data
);
786 sheet
->style_data
= NULL
;
788 if (--active_sheet_count
== 0) {
790 go_mem_chunk_foreach_leak (tile_pools
[TILE_SIMPLE
],
791 cb_tile_pool_leak
, NULL
);
792 go_mem_chunk_destroy (tile_pools
[TILE_SIMPLE
], FALSE
);
793 tile_pools
[TILE_SIMPLE
] = NULL
;
795 go_mem_chunk_foreach_leak (tile_pools
[TILE_COL
],
796 cb_tile_pool_leak
, NULL
);
797 go_mem_chunk_destroy (tile_pools
[TILE_COL
], FALSE
);
798 tile_pools
[TILE_COL
] = NULL
;
800 go_mem_chunk_foreach_leak (tile_pools
[TILE_ROW
],
801 cb_tile_pool_leak
, NULL
);
802 go_mem_chunk_destroy (tile_pools
[TILE_ROW
], FALSE
);
803 tile_pools
[TILE_ROW
] = NULL
;
805 go_mem_chunk_foreach_leak (tile_pools
[TILE_MATRIX
],
806 cb_tile_pool_leak
, NULL
);
807 go_mem_chunk_destroy (tile_pools
[TILE_MATRIX
], FALSE
);
808 tile_pools
[TILE_MATRIX
] = NULL
;
810 /* If this fails one day, just make two pools. */
811 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
812 tile_pools
[TILE_PTR_MATRIX
] = NULL
;
814 if (tile_allocations
)
815 g_printerr ("Leaking %d style tiles.\n", tile_allocations
);
821 * sheet_style_set_auto_pattern_color:
823 * @grid_color: (transfer full): The color
825 * Set the color for rendering auto colored patterns in this sheet.
828 sheet_style_set_auto_pattern_color (Sheet
*sheet
, GnmColor
*pattern_color
)
830 g_return_if_fail (IS_SHEET (sheet
));
831 g_return_if_fail (sheet
->style_data
!= NULL
);
833 style_color_unref (sheet
->style_data
->auto_pattern_color
);
834 sheet
->style_data
->auto_pattern_color
= gnm_color_new_auto (pattern_color
->go_color
);
835 style_color_unref (pattern_color
);
839 * sheet_style_get_auto_pattern_color:
842 * Returns: (transfer full): the color for rendering auto colored patterns
846 sheet_style_get_auto_pattern_color (Sheet
const *sheet
)
849 g_return_val_if_fail (IS_SHEET (sheet
), style_color_black ());
850 g_return_val_if_fail (sheet
->style_data
!= NULL
, style_color_black ());
851 g_return_val_if_fail (sheet
->style_data
->auto_pattern_color
!= NULL
,
852 style_color_black ());
854 sc
= sheet
->style_data
->auto_pattern_color
;
855 style_color_ref (sc
);
861 * sheet_style_update_grid_color:
863 * This function updates the color of gnm_style_border_none when the sheet to be
864 * rendered is known. gnm_style_border_none tells how to render the
865 * grid. Because the grid color may be different for different sheets, the
866 * functions which render the grid call this function first. The rule for
867 * selecting the grid color, which is the same as in Excel, is: - if the
868 * auto pattern color is default (which is black), the grid color is gray,
869 * as returned by style_color_grid (). - otherwise, the auto pattern color
870 * is used for the grid.
873 sheet_style_update_grid_color (Sheet
const *sheet
)
875 GnmColor
*default_auto
= style_color_auto_pattern ();
876 GnmColor
*sheet_auto
= sheet_style_get_auto_pattern_color (sheet
);
877 GnmColor
*grid_color
= style_color_grid ();
880 new_color
= (style_color_equal (default_auto
, sheet_auto
)
881 ? grid_color
: sheet_auto
);
883 /* Do nothing if we already have the right color */
884 if (gnm_style_border_none()->color
!= new_color
) {
885 style_color_ref (new_color
); /* none_set eats the ref */
886 gnm_style_border_none_set_color (new_color
);
888 style_color_unref (grid_color
);
889 style_color_unref (sheet_auto
);
890 style_color_unref (default_auto
);
893 /****************************************************************************/
896 tile_is_uniform (CellTile
const *tile
)
898 const int s
= tile_size
[tile
->type
];
899 GnmStyle
const *st
= tile
->style_any
.style
[0];
902 for (i
= 1; i
< s
; i
++)
903 if (tile
->style_any
.style
[i
] != st
)
910 vector_apply_pstyle (CellTile
*tile
, ReplacementStyle
*rs
,
911 int cc
, int cr
, int level
, GnmRange
const *indic
)
913 const CellTileType type
= tile
->type
;
914 const int ncols
= tile_col_count
[type
];
915 const int nrows
= tile_row_count
[type
];
916 const int w1
= tile_widths
[level
+ 1] / ncols
;
917 const int h1
= tile_heights
[level
+ 1] / nrows
;
918 const int fcol
= indic
->start
.col
;
919 const int frow
= indic
->start
.row
;
920 const int lcol
= MIN (ncols
- 1, indic
->end
.col
);
921 const int lrow
= MIN (nrows
- 1, indic
->end
.row
);
922 GnmSheetSize
const *ss
= gnm_sheet_get_size (rs
->sheet
);
926 for (r
= frow
; r
<= lrow
; r
++) {
927 GnmStyle
**st
= tile
->style_any
.style
+ ncols
* r
;
928 rng
.start
.row
= cr
+ h1
* r
;
929 rng
.end
.row
= MIN (rng
.start
.row
+ (h1
- 1),
931 for (c
= fcol
; c
<= lcol
; c
++) {
932 rng
.start
.col
= cc
+ w1
* c
;
933 rng
.end
.col
= MIN (rng
.start
.col
+ (w1
- 1),
935 rstyle_apply (st
+ c
, rs
, &rng
);
941 * Determine whether before applying a style in the area of apply_to
942 * one needs to split the tile column-wise.
944 * If FALSE is returned then the tile need to be split to a TILE_PTR_MATRIX
945 * because the current level is not fine-grained enough.
947 * If TRUE is returned, TILE_SIMPLE needs to be split into TILE_COL and
948 * TILE_ROW needs to be split into TILE_MATRIX. TILE_COL and TILE_MATRIX
949 * should be kept. In indic, the inclusive post-split indicies of the
950 * range will be returned.
952 * If apply_to covers the entire tile, TRUE will be returned and the judgement
953 * on splitting above should be ignored. The indices in indic will be as-if
954 * the split was done.
957 col_indicies (int corner_col
, int w
, GnmRange
const *apply_to
,
962 i
= apply_to
->start
.col
- corner_col
;
964 indec
->start
.col
= 0;
969 indec
->start
.col
= tmp
;
972 i
= 1 + apply_to
->end
.col
- corner_col
;
974 if (tmp
>= TILE_SIZE_COL
)
975 indec
->end
.col
= TILE_SIZE_COL
- 1;
979 indec
->end
.col
= tmp
- 1;
985 /* See docs for col_indicies. Swap cols and rows. */
987 row_indicies (int corner_row
, int h
, GnmRange
const *apply_to
,
992 i
= apply_to
->start
.row
- corner_row
;
994 indic
->start
.row
= 0;
999 indic
->start
.row
= tmp
;
1002 i
= 1 + apply_to
->end
.row
- corner_row
;
1004 if (tmp
>= TILE_SIZE_ROW
)
1005 indic
->end
.row
= TILE_SIZE_ROW
- 1;
1009 indic
->end
.row
= tmp
- 1;
1016 * cell_tile_apply: This is the primary logic for making changing areas in the
1017 * tree. It could be further optimised if it becomes a bottle neck.
1020 cell_tile_apply (CellTile
**tile
, int level
,
1021 int corner_col
, int corner_row
,
1022 GnmRange
const *apply_to
,
1023 ReplacementStyle
*rs
)
1025 int const width
= tile_widths
[level
+1];
1026 int const height
= tile_heights
[level
+1];
1027 int const w
= tile_widths
[level
];
1028 int const h
= tile_heights
[level
];
1029 gboolean
const full_width
= (apply_to
->start
.col
<= corner_col
&&
1030 apply_to
->end
.col
>= (corner_col
+width
-1));
1031 gboolean
const full_height
= (apply_to
->start
.row
<= corner_row
&&
1032 apply_to
->end
.row
>= (corner_row
+height
-1));
1037 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1038 g_return_if_fail (tile
!= NULL
);
1039 g_return_if_fail (*tile
!= NULL
);
1041 type
= (*tile
)->type
;
1042 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1044 /* applying the same style to part of a simple-tile is a nop */
1045 if (type
== TILE_SIMPLE
&&
1046 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1050 * Indices for the whole tile assuming a split to matrix.
1051 * We can still use these indices if we don't split either way.
1053 indic
.start
.col
= 0;
1054 indic
.start
.row
= 0;
1055 indic
.end
.col
= TILE_SIZE_COL
- 1;
1056 indic
.end
.row
= TILE_SIZE_ROW
- 1;
1058 if (type
== TILE_PTR_MATRIX
)
1060 else if (full_width
&& full_height
)
1062 else if (full_height
) {
1063 if (!col_indicies (corner_col
, w
, apply_to
, &indic
))
1064 goto split_to_ptr_matrix
;
1070 res
= cell_tile_style_new (
1071 (*tile
)->style_simple
.style
[0],
1073 cell_tile_dtor (*tile
);
1081 goto split_to_matrix
;
1083 g_assert_not_reached ();
1085 } else if (full_width
) {
1086 if (!row_indicies (corner_row
, h
, apply_to
, &indic
))
1087 goto split_to_ptr_matrix
;
1093 res
= cell_tile_style_new (
1094 (*tile
)->style_simple
.style
[0],
1096 cell_tile_dtor (*tile
);
1104 goto split_to_matrix
;
1106 g_assert_not_reached ();
1109 if (col_indicies (corner_col
, w
, apply_to
, &indic
) &&
1110 row_indicies (corner_row
, h
, apply_to
, &indic
))
1111 goto split_to_matrix
;
1113 goto split_to_ptr_matrix
;
1116 g_assert_not_reached ();
1119 *tile
= cell_tile_matrix_set (*tile
);
1122 vector_apply_pstyle (*tile
, rs
, corner_col
, corner_row
, level
, &indic
);
1126 CellTileOptimize cto
;
1127 cto
.ss
= gnm_sheet_get_size (rs
->sheet
);
1128 cto
.recursion
= FALSE
;
1129 cell_tile_optimize (tile
, level
, &cto
, corner_col
, corner_row
);
1133 split_to_ptr_matrix
:
1135 * We get here when apply_to's corners are not on a TILE_MATRIX grid.
1136 * Split to pointer matrix whose element tiles will have a finer grid.
1138 g_return_if_fail (type
!= TILE_PTR_MATRIX
);
1140 CellTile
*res
= cell_tile_ptr_matrix_new (*tile
);
1141 cell_tile_dtor (*tile
);
1143 type
= TILE_PTR_MATRIX
;
1147 g_return_if_fail (type
== TILE_PTR_MATRIX
);
1148 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1149 int const cr
= corner_row
+ h
*r
;
1150 if (cr
> apply_to
->end
.row
)
1152 if ((cr
+ h
) <= apply_to
->start
.row
)
1155 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1156 int const cc
= corner_col
+ w
*c
;
1157 if (cc
> apply_to
->end
.col
)
1159 if ((cc
+ w
) <= apply_to
->start
.col
)
1162 cell_tile_apply ((*tile
)->ptr_matrix
.ptr
+ i
+ c
,
1163 level
- 1, cc
, cr
, apply_to
, rs
);
1169 /* Handler for foreach_tile.
1171 * "width" and "height" refer to tile size which may extend beyond
1172 * the range supplied to foreach_tile and even beyond the sheet.
1174 typedef void (*ForeachTileFunc
) (GnmStyle
*style
,
1175 int corner_col
, int corner_row
,
1176 int width
, int height
,
1177 GnmRange
const *apply_to
, gpointer user
);
1179 foreach_tile_r (CellTile
*tile
, int level
,
1180 int corner_col
, int corner_row
,
1181 GnmRange
const *apply_to
,
1182 ForeachTileFunc handler
,
1185 int const width
= tile_widths
[level
+1];
1186 int const height
= tile_heights
[level
+1];
1187 int const w
= tile_widths
[level
];
1188 int const h
= tile_heights
[level
];
1191 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1192 g_return_if_fail (tile
!= NULL
);
1194 switch (tile
->type
) {
1196 handler (tile
->style_simple
.style
[0],
1197 corner_col
, corner_row
, width
, height
,
1202 if (apply_to
!= NULL
) {
1203 c
= (apply_to
->start
.col
- corner_col
) / w
;
1206 last
= (apply_to
->end
.col
- corner_col
) / w
+ 1;
1207 if (last
> TILE_SIZE_COL
)
1208 last
= TILE_SIZE_COL
;
1211 last
= TILE_SIZE_COL
;
1213 for (; c
< last
; ++c
)
1214 handler (tile
->style_col
.style
[c
],
1215 corner_col
+ c
*w
, corner_row
, w
, height
,
1220 if (apply_to
!= NULL
) {
1221 r
= (apply_to
->start
.row
- corner_row
) / h
;
1224 last
= (apply_to
->end
.row
- corner_row
) / h
+ 1;
1225 if (last
> TILE_SIZE_ROW
)
1226 last
= TILE_SIZE_ROW
;
1229 last
= TILE_SIZE_ROW
;
1231 for (; r
< last
; ++r
)
1232 handler (tile
->style_row
.style
[r
],
1233 corner_col
, corner_row
+ r
*h
, width
, h
,
1238 case TILE_PTR_MATRIX
:
1239 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1240 int const cr
= corner_row
+ h
*r
;
1242 if (cr
> apply_to
->end
.row
)
1244 if ((cr
+ h
) <= apply_to
->start
.row
)
1248 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1249 int const cc
= corner_col
+ w
*c
;
1251 if (cc
> apply_to
->end
.col
)
1253 if ((cc
+ w
) <= apply_to
->start
.col
)
1257 if (tile
->type
== TILE_MATRIX
) {
1258 handler (tile
->style_matrix
.style
[r
*TILE_SIZE_COL
+c
],
1261 w
, h
, apply_to
, user
);
1264 tile
->ptr_matrix
.ptr
[c
+ r
*TILE_SIZE_COL
],
1265 level
-1, cc
, cr
, apply_to
, handler
, user
);
1272 g_warning ("Adaptive Quad Tree corruption !");
1277 foreach_tile (Sheet
const *sheet
, GnmRange
const *apply_to
,
1278 ForeachTileFunc handler
, gpointer user
)
1280 foreach_tile_r (sheet
->style_data
->styles
,
1281 sheet
->tile_top_level
, 0, 0,
1282 apply_to
, handler
, user
);
1286 * cell_tile_apply_pos: This is an simplified version of cell_tile_apply. It
1287 * does not need all the bells and whistles because it operates on single cells.
1290 cell_tile_apply_pos (CellTile
**tile
, int level
,
1292 ReplacementStyle
*rs
)
1298 g_return_if_fail (col
>= 0);
1299 g_return_if_fail (col
< gnm_sheet_get_max_cols (rs
->sheet
));
1300 g_return_if_fail (row
>= 0);
1301 g_return_if_fail (row
< gnm_sheet_get_max_rows (rs
->sheet
));
1303 range_init (&rng
, col
, row
, col
, row
);
1306 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1307 g_return_if_fail (tile
!= NULL
);
1308 g_return_if_fail (*tile
!= NULL
);
1312 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1315 int const w
= tile_widths
[level
];
1316 int const c
= col
/ w
;
1317 int const h
= tile_heights
[level
];
1318 int const r
= row
/ h
;
1320 if (type
!= TILE_PTR_MATRIX
) {
1321 /* applying the same style to part of a simple-tile is a nop */
1322 if (type
== TILE_SIMPLE
&&
1323 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1326 tmp
= cell_tile_ptr_matrix_new (tmp
);
1327 cell_tile_dtor (*tile
);
1330 tile
= tmp
->ptr_matrix
.ptr
+ r
* TILE_SIZE_COL
+ c
;
1334 goto tail_recursion
;
1335 } else if (type
!= TILE_MATRIX
)
1336 *tile
= tmp
= cell_tile_matrix_set (tmp
);
1338 g_return_if_fail (tmp
->type
== TILE_MATRIX
);
1339 rstyle_apply (tmp
->style_matrix
.style
+ row
* TILE_SIZE_COL
+ col
,
1345 * sheet_style_set_range:
1346 * @sheet: #Sheet being changed
1347 * @range: #GnmRange being changed
1348 * @style: (transfer full): New #GnmStyle
1350 * Change the complete style for a region.
1353 sheet_style_set_range (Sheet
*sheet
, GnmRange
const *range
,
1356 ReplacementStyle rs
;
1359 g_return_if_fail (IS_SHEET (sheet
));
1360 g_return_if_fail (range
!= NULL
);
1362 if (range
->start
.col
> range
->end
.col
||
1363 range
->start
.row
> range
->end
.row
) {
1364 gnm_style_unref (style
);
1369 range_ensure_sanity (&r
, sheet
);
1371 rstyle_ctor_style (&rs
, style
, sheet
);
1372 cell_tile_apply (&sheet
->style_data
->styles
,
1373 sheet
->tile_top_level
, 0, 0,
1379 * sheet_style_apply_col:
1380 * @sheet: #Sheet being changed
1382 * @style: (transfer full): #GnmStyle
1384 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1385 * will make life easier.
1387 * Apply a partial style to a full col.
1390 sheet_style_apply_col (Sheet
*sheet
, int col
, GnmStyle
*pstyle
)
1393 range_init_cols (&r
, sheet
, col
, col
);
1394 sheet_style_apply_range (sheet
, &r
, pstyle
);
1398 * sheet_style_apply_row:
1401 * @style: (transfer full): #GnmStyle
1403 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1404 * will make life easier.
1406 * Apply a partial style to a full col.
1409 sheet_style_apply_row (Sheet
*sheet
, int row
, GnmStyle
*pstyle
)
1412 range_init_rows (&r
, sheet
, row
, row
);
1413 sheet_style_apply_range (sheet
, &r
, pstyle
);
1417 * sheet_style_apply_pos:
1421 * @style: (transfer full): #GnmStyle
1423 * Apply a partial style to a single cell
1426 sheet_style_apply_pos (Sheet
*sheet
, int col
, int row
, GnmStyle
*pstyle
)
1428 ReplacementStyle rs
;
1430 g_return_if_fail (IS_SHEET (sheet
));
1432 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1433 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1434 sheet
->tile_top_level
, col
, row
,
1439 * sheet_style_set_pos:
1443 * @style: (transfer full):
1445 * Change the complete style for a single cell.
1448 sheet_style_set_pos (Sheet
*sheet
, int col
, int row
,
1451 ReplacementStyle rs
;
1453 g_return_if_fail (IS_SHEET (sheet
));
1455 rstyle_ctor_style (&rs
, style
, sheet
);
1456 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1457 sheet
->tile_top_level
, col
, row
,
1463 * sheet_style_default:
1466 * Returns a reference to default style for a sheet.
1469 sheet_style_default (Sheet
const *sheet
)
1471 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1472 g_return_val_if_fail (sheet
->style_data
!= NULL
, NULL
);
1474 gnm_style_ref (sheet
->style_data
->default_style
);
1475 return sheet
->style_data
->default_style
;
1481 * @col: column number
1484 * Returns: (transfer none): find the fully qualified style applicable to
1485 * the specified cell position
1488 sheet_style_get (Sheet
const *sheet
, int col
, int row
)
1490 int level
= sheet
->tile_top_level
;
1491 CellTile
*tile
= sheet
->style_data
->styles
;
1494 int width
= tile_widths
[level
];
1495 int height
= tile_heights
[level
];
1496 int c
= col
/ width
;
1497 int r
= row
/ height
;
1499 g_return_val_if_fail (tile
!= NULL
, NULL
);
1500 g_return_val_if_fail (0 <= c
&& c
< TILE_SIZE_COL
, NULL
);
1501 g_return_val_if_fail (0 <= r
&& r
< TILE_SIZE_ROW
, NULL
);
1503 switch (tile
->type
) {
1505 return tile
->style_simple
.style
[0];
1507 return tile
->style_col
.style
[c
];
1509 return tile
->style_row
.style
[r
];
1511 return tile
->style_matrix
.style
[r
* TILE_SIZE_COL
+ c
];
1513 case TILE_PTR_MATRIX
:
1514 g_return_val_if_fail (level
> 0, NULL
);
1517 tile
= tile
->ptr_matrix
.ptr
[r
* TILE_SIZE_COL
+ c
];
1523 g_warning ("Adaptive Quad Tree corruption !");
1529 #define border_null(b) ((b) == none || (b) == NULL)
1532 style_row (GnmStyle
const *style
, int start_col
, int end_col
,
1533 GnmStyleRow
*sr
, gboolean accept_conditions
)
1535 GnmBorder
const *top
, *bottom
, *none
= gnm_style_border_none ();
1536 GnmBorder
const *left
, *right
, *v
;
1537 int const end
= MIN (end_col
, sr
->end_col
);
1538 int i
= MAX (start_col
, sr
->start_col
);
1539 GnmStyleConditions
*conds
;
1541 conds
= accept_conditions
1542 ? gnm_style_get_conditions (style
)
1548 for (eval_pos_init (&ep
, (Sheet
*)sr
->sheet
, i
, sr
->row
); ep
.eval
.col
<= end
; ep
.eval
.col
++) {
1549 res
= gnm_style_conditions_eval (conds
, &ep
);
1551 ? gnm_style_get_cond_style (style
, res
)
1553 ep
.eval
.col
, ep
.eval
.col
, sr
, FALSE
);
1558 top
= gnm_style_get_border (style
, MSTYLE_BORDER_TOP
);
1559 bottom
= gnm_style_get_border (style
, MSTYLE_BORDER_BOTTOM
);
1560 left
= gnm_style_get_border (style
, MSTYLE_BORDER_LEFT
);
1561 right
= gnm_style_get_border (style
, MSTYLE_BORDER_RIGHT
);
1563 /* Cancel grids if there is a background */
1564 if (sr
->hide_grid
|| gnm_style_get_pattern (style
) > 0) {
1575 if (left
!= none
&& border_null (sr
->vertical
[i
]))
1576 sr
->vertical
[i
] = left
;
1577 v
= border_null (right
) ? left
: right
;
1580 sr
->styles
[i
] = style
;
1581 if (top
!= none
&& border_null (sr
->top
[i
]))
1583 sr
->bottom
[i
] = bottom
;
1584 sr
->vertical
[++i
] = v
;
1586 if (border_null (right
))
1587 sr
->vertical
[i
] = right
;
1591 get_style_row (CellTile
const *tile
, int level
,
1592 int corner_col
, int corner_row
,
1595 int const width
= tile_widths
[level
+1];
1596 int const w
= tile_widths
[level
];
1597 int const h
= tile_heights
[level
];
1601 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1602 g_return_if_fail (tile
!= NULL
);
1606 if (t
!= TILE_SIMPLE
&& t
!= TILE_COL
) {
1607 r
= (sr
->row
> corner_row
) ? (sr
->row
- corner_row
)/ h
: 0;
1608 g_return_if_fail (r
< TILE_SIZE_ROW
);
1611 if (t
== TILE_ROW
|| t
== TILE_SIMPLE
) {
1612 style_row (tile
->style_any
.style
[r
],
1613 corner_col
, corner_col
+ width
- 1, sr
, TRUE
);
1615 /* find the start and end */
1617 int last_c
= (sr
->end_col
- corner_col
) / w
;
1618 if (last_c
>= TILE_SIZE_COL
)
1619 last_c
= TILE_SIZE_COL
-1;
1620 if (sr
->start_col
> corner_col
) {
1621 c
= (sr
->start_col
- corner_col
) / w
;
1622 corner_col
+= c
* w
;
1628 if (t
!= TILE_PTR_MATRIX
) {
1629 GnmStyle
* const *styles
= tile
->style_any
.style
+ r
*TILE_SIZE_COL
;
1631 for ( ; c
<= last_c
; c
++, corner_col
+= w
)
1632 style_row (styles
[c
],
1633 corner_col
, corner_col
+ w
- 1, sr
, TRUE
);
1635 CellTile
* const *tiles
= tile
->ptr_matrix
.ptr
+ r
*TILE_SIZE_COL
;
1637 g_return_if_fail (level
> 0);
1639 for ( level
-- ; c
<= last_c
; c
++, corner_col
+= w
)
1640 get_style_row (tiles
[c
], level
,
1641 corner_col
, corner_row
, sr
);
1647 * sheet_style_get_row:
1651 * A utility routine which efficiently retrieves a range of styles within a row.
1652 * It also merges adjacent borders as necessary.
1655 sheet_style_get_row (Sheet
const *sheet
, GnmStyleRow
*sr
)
1658 g_return_if_fail (IS_SHEET (sheet
));
1659 g_return_if_fail (sr
!= NULL
);
1660 g_return_if_fail (sr
->styles
!= NULL
);
1661 g_return_if_fail (sr
->vertical
!= NULL
);
1662 g_return_if_fail (sr
->top
!= NULL
);
1663 g_return_if_fail (sr
->bottom
!= NULL
);
1666 sr
->vertical
[sr
->start_col
] = gnm_style_border_none ();
1667 get_style_row (sheet
->style_data
->styles
, sheet
->tile_top_level
, 0, 0, sr
);
1671 cb_get_row (GnmStyle
*style
,
1672 int corner_col
, G_GNUC_UNUSED
int corner_row
,
1673 int width
, G_GNUC_UNUSED
int height
,
1674 GnmRange
const *apply_to
, gpointer user_
)
1676 GnmStyle
**res
= user_
;
1679 /* The given dimensions refer to the tile, not the area. */
1680 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
1682 for (i
= 0; i
< width
; i
++)
1683 res
[corner_col
+ i
] = style
;
1687 sheet_style_get_row2 (Sheet
const *sheet
, int row
)
1690 GnmStyle
**res
= g_new (GnmStyle
*, gnm_sheet_get_max_cols (sheet
));
1692 range_init_rows (&r
, sheet
, row
, row
);
1694 foreach_tile (sheet
, &r
, cb_get_row
, res
);
1703 * A small utility routine to initialize the grid drawing GnmStyleRow data
1707 style_row_init (GnmBorder
const * * *prev_vert
,
1708 GnmStyleRow
*sr
, GnmStyleRow
*next_sr
,
1709 int start_col
, int end_col
, gpointer mem
, gboolean hide_grid
)
1712 GnmBorder
const *none
= hide_grid
? NULL
: gnm_style_border_none ();
1714 /* alias the arrays for easy access so that array[col] is valid
1715 * for all elements start_col-1 .. end_col+1 inclusive.
1716 * Note that this means that in some cases array[-1] is legal.
1718 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
1720 sr
->vertical
-= start_col
-1;
1721 sr
->top
= sr
->vertical
+ n
;
1722 sr
->bottom
= sr
->top
+ n
;
1723 next_sr
->top
= sr
->bottom
; /* yes they should share */
1724 next_sr
->bottom
= next_sr
->top
+ n
;
1725 next_sr
->vertical
= next_sr
->bottom
+ n
;
1726 *prev_vert
= next_sr
->vertical
+ n
;
1727 sr
->styles
= ((GnmStyle
const **) (*prev_vert
+ n
));
1728 next_sr
->styles
= sr
->styles
+ n
;
1729 sr
->start_col
= next_sr
->start_col
= start_col
;
1730 sr
->end_col
= next_sr
->end_col
= end_col
;
1731 sr
->hide_grid
= next_sr
->hide_grid
= hide_grid
;
1733 /* Init the areas that sheet_style_get_row will not */
1734 for (col
= start_col
-1 ; col
<= end_col
+1; ++col
)
1735 (*prev_vert
)[col
] = sr
->top
[col
] = none
;
1736 sr
->vertical
[start_col
-1] = sr
->vertical
[end_col
+1] =
1737 next_sr
->vertical
[start_col
-1] = next_sr
->vertical
[end_col
+1] =
1738 next_sr
->top
[start_col
-1] = next_sr
->top
[end_col
+1] =
1739 next_sr
->bottom
[start_col
-1] = next_sr
->bottom
[end_col
+1] = none
;
1743 * sheet_style_apply_range: (skip)
1745 * @range: #GnmRange to apply over
1746 * @pstyle: (transfer full): A partial style to apply
1748 * Apply a partial style to a region.
1751 sheet_style_apply_range (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1753 ReplacementStyle rs
;
1756 g_return_if_fail (IS_SHEET (sheet
));
1757 g_return_if_fail (range
!= NULL
);
1759 if (range
->start
.col
> range
->end
.col
||
1760 range
->start
.row
> range
->end
.row
) {
1761 gnm_style_unref (pstyle
);
1766 range_ensure_sanity (&r
, sheet
);
1768 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1769 cell_tile_apply (&sheet
->style_data
->styles
,
1770 sheet
->tile_top_level
, 0, 0,
1776 * sheet_style_apply_range2: (skip)
1778 * @range: #GnmRange to apply over
1779 * @pstyle: (transfer none): A partial style to apply
1781 * Apply a partial style to a region.
1784 sheet_style_apply_range2 (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1786 gnm_style_ref (pstyle
);
1787 sheet_style_apply_range (sheet
, range
, pstyle
);
1792 apply_border (Sheet
*sheet
, GnmRange
const *r
,
1793 GnmStyleBorderLocation side
,
1796 GnmStyle
*pstyle
= gnm_style_new ();
1797 pstyle_set_border (pstyle
, border
, side
);
1798 sheet_style_apply_range (sheet
, r
, pstyle
);
1802 * sheet_style_apply_border:
1807 * When a user applies a border to a region we attempt to remove the border
1808 * from the opposing side to avoid overlapping border specifications.
1810 * if we apply a top border to a range, we would clear the bottom border
1811 * of the range offset upwards.
1814 sheet_style_apply_border (Sheet
*sheet
,
1815 GnmRange
const *range
,
1816 GnmBorder
**borders
)
1818 GnmStyle
*pstyle
= NULL
;
1820 if (borders
== NULL
)
1823 if (borders
[GNM_STYLE_BORDER_TOP
]) {
1825 GnmRange r
= *range
;
1826 r
.end
.row
= r
.start
.row
;
1827 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1828 borders
[GNM_STYLE_BORDER_TOP
]);
1832 if (r
.start
.row
>= 0) {
1833 r
.end
.row
= r
.start
.row
;
1834 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1835 gnm_style_border_none ());
1839 if (borders
[GNM_STYLE_BORDER_BOTTOM
]) {
1840 /* 2.1 bottom inner */
1841 GnmRange r
= *range
;
1842 r
.start
.row
= r
.end
.row
;
1843 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1844 borders
[GNM_STYLE_BORDER_BOTTOM
]);
1846 /* 2.2 bottom outer */
1848 if (r
.end
.row
< gnm_sheet_get_last_row (sheet
)) {
1849 r
.start
.row
= r
.end
.row
;
1850 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1851 gnm_style_border_none ());
1855 if (borders
[GNM_STYLE_BORDER_LEFT
]) {
1856 /* 3.1 left inner */
1857 GnmRange r
= *range
;
1858 r
.end
.col
= r
.start
.col
;
1859 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1860 borders
[GNM_STYLE_BORDER_LEFT
]);
1862 /* 3.2 left outer */
1864 if (r
.start
.col
>= 0) {
1865 r
.end
.col
= r
.start
.col
;
1866 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1867 gnm_style_border_none ());
1871 if (borders
[GNM_STYLE_BORDER_RIGHT
]) {
1872 /* 4.1 right inner */
1873 GnmRange r
= *range
;
1874 r
.start
.col
= r
.end
.col
;
1875 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1876 borders
[GNM_STYLE_BORDER_RIGHT
]);
1878 /* 4.2 right outer */
1880 if (r
.end
.col
< gnm_sheet_get_last_col (sheet
)) {
1881 r
.start
.col
= r
.end
.col
;
1882 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1883 gnm_style_border_none ());
1887 /* Interiors horizontal : prefer top */
1888 if (borders
[GNM_STYLE_BORDER_HORIZ
] != NULL
) {
1889 /* 5.1 horizontal interior top */
1890 if (range
->start
.row
!= range
->end
.row
) {
1891 GnmRange r
= *range
;
1893 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1894 borders
[GNM_STYLE_BORDER_HORIZ
]);
1896 /* 5.2 interior bottom */
1897 if (range
->start
.row
!= range
->end
.row
) {
1898 GnmRange r
= *range
;
1900 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1901 gnm_style_border_none ());
1905 /* Interiors vertical: prefer left */
1906 if (borders
[GNM_STYLE_BORDER_VERT
] != NULL
) {
1907 /* 6.1 vertical interior left */
1908 if (range
->start
.col
!= range
->end
.col
) {
1909 GnmRange r
= *range
;
1911 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1912 borders
[GNM_STYLE_BORDER_VERT
]);
1915 /* 6.2 The vertical interior right */
1916 if (range
->start
.col
!= range
->end
.col
) {
1917 GnmRange r
= *range
;
1919 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1920 gnm_style_border_none ());
1924 /* 7. Diagonals (apply both in one pass) */
1925 if (borders
[GNM_STYLE_BORDER_DIAG
] != NULL
) {
1926 pstyle
= gnm_style_new ();
1927 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_DIAG
],
1928 GNM_STYLE_BORDER_DIAG
);
1930 if (borders
[GNM_STYLE_BORDER_REV_DIAG
]) {
1932 pstyle
= gnm_style_new ();
1933 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_REV_DIAG
],
1934 GNM_STYLE_BORDER_REV_DIAG
);
1937 sheet_style_apply_range (sheet
, range
, pstyle
);
1940 /****************************************************************************/
1944 unsigned int conflicts
;
1948 cb_find_conflicts (GnmStyle
*style
,
1949 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
1950 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
1951 G_GNUC_UNUSED GnmRange
const *apply_to
, FindConflicts
*ptr
)
1953 ptr
->conflicts
= gnm_style_find_conflicts (ptr
->accum
, style
, ptr
->conflicts
);
1957 border_mask_internal (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1958 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1959 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1963 gnm_style_border_unref (borders
[l
]);
1964 borders
[l
] = (GnmBorder
*)b
;
1965 gnm_style_border_ref (borders
[l
]);
1966 } else if (borders
[l
] != b
&& borders
[l
] != NULL
) {
1967 gnm_style_border_unref (borders
[l
]);
1973 border_mask (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1974 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1975 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1978 b
= gnm_style_border_none ();
1979 border_mask_internal (known
, borders
, b
, l
);
1983 border_mask_vec (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1984 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1985 GnmBorder
const * const *vec
, int first
, int last
,
1986 GnmStyleBorderLocation l
)
1988 GnmBorder
const *b
= vec
[first
];
1991 b
= gnm_style_border_none ();
1992 while (first
++ < last
) {
1993 GnmBorder
const *tmp
= vec
[first
];
1995 tmp
= gnm_style_border_none ();
2002 border_mask_internal (known
, borders
, b
, l
);
2006 * sheet_style_find_conflicts:
2007 * @sheet: #Sheet to query
2008 * @r: #GnmRange to query
2010 * @borders: (out) (array fixed-size=8):
2012 * Returns: bitmask of conflicts
2015 sheet_style_find_conflicts (Sheet
const *sheet
, GnmRange
const *r
,
2017 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
])
2019 int n
, col
, row
, start_col
, end_col
;
2021 gpointer
*sr_array_data
;
2022 GnmStyleBorderLocation i
;
2023 gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
];
2024 GnmBorder
const *none
= gnm_style_border_none ();
2027 g_return_val_if_fail (IS_SHEET (sheet
), 0);
2028 g_return_val_if_fail (r
!= NULL
, 0);
2029 g_return_val_if_fail (style
!= NULL
, 0);
2030 g_return_val_if_fail (borders
!= NULL
, 0);
2032 /* init style set with a copy of the top left corner of the 1st range */
2033 if (*style
== NULL
) {
2034 GnmStyle
const *tmp
= sheet_style_get (sheet
, r
->start
.col
, r
->start
.row
);
2035 *style
= gnm_style_dup (tmp
);
2036 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2038 borders
[i
] = gnm_style_border_ref ((GnmBorder
*)none
);
2041 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2047 user
.accum
= *style
;
2048 user
.conflicts
= 0; /* no conflicts yet */
2049 foreach_tile (sheet
, r
, (ForeachTileFunc
)cb_find_conflicts
, &user
);
2051 /* copy over the diagonals */
2052 for (i
= GNM_STYLE_BORDER_REV_DIAG
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2053 GnmStyleElement se
= GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (i
);
2054 gnm_style_border_unref (borders
[i
]);
2055 if (user
.conflicts
& (1 << se
))
2058 borders
[i
] = gnm_style_border_ref (
2059 gnm_style_get_border (*style
, se
));
2062 start_col
= r
->start
.col
;
2063 if (r
->start
.col
> 0)
2065 end_col
= r
->end
.col
;
2066 if (r
->end
.col
< gnm_sheet_get_max_cols (sheet
))
2069 /* allocate then alias the arrays for easy access */
2070 n
= end_col
- start_col
+ 2;
2071 g_assert (sizeof (GnmBorder
*) == sizeof (gpointer
));
2072 g_assert (sizeof (GnmStyle
*) == sizeof (gpointer
));
2073 sr_array_data
= g_new (gpointer
, n
* 4);
2074 sr
.vertical
= (GnmBorder
const **)(sr_array_data
- start_col
);
2075 sr
.top
= (GnmBorder
const **)(sr_array_data
+ n
- start_col
);
2076 sr
.bottom
= (GnmBorder
const **)(sr_array_data
+ 2 * n
- start_col
);
2077 sr
.styles
= (GnmStyle
const **) (sr_array_data
+ 3 * n
- start_col
);
2078 sr
.start_col
= start_col
;
2079 sr
.end_col
= end_col
;
2080 sr
.hide_grid
= sheet
->hide_grid
;
2082 /* pretend the previous bottom had no borders */
2083 for (col
= start_col
; col
<= end_col
; ++col
)
2086 /* merge the bottom of the previous row */
2087 if (r
->start
.row
> 0) {
2088 GnmBorder
const ** roller
;
2089 sr
.row
= r
->start
.row
- 1;
2090 sheet_style_get_row (sheet
, &sr
);
2091 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2095 * TODO: The border handling is tricky and currently VERY slow for
2096 * large ranges. We could easily optimize this. There is no need to
2097 * retrieve the style in every cell just to do a filter for uniformity
2098 * by row. One day we should do a special case version of
2099 * sheet_style_get_row probably style_get_uniform_col (this will be
2102 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
2103 GnmBorder
const **roller
;
2105 sheet_style_get_row (sheet
, &sr
);
2107 border_mask (known
, borders
, sr
.vertical
[r
->start
.col
],
2108 GNM_STYLE_BORDER_LEFT
);
2109 border_mask (known
, borders
, sr
.vertical
[r
->end
.col
+1],
2110 GNM_STYLE_BORDER_RIGHT
);
2111 border_mask_vec (known
, borders
, sr
.top
,
2112 r
->start
.col
, r
->end
.col
, (row
== r
->start
.row
)
2113 ? GNM_STYLE_BORDER_TOP
: GNM_STYLE_BORDER_HORIZ
);
2114 if (r
->start
.col
!= r
->end
.col
)
2115 border_mask_vec (known
, borders
, sr
.vertical
,
2116 r
->start
.col
+1, r
->end
.col
,
2117 GNM_STYLE_BORDER_VERT
);
2119 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2122 /* merge the top of the next row */
2123 if (r
->end
.row
< gnm_sheet_get_last_row (sheet
)) {
2124 sr
.row
= r
->end
.row
+ 1;
2125 sheet_style_get_row (sheet
, &sr
);
2127 border_mask_vec (known
, borders
, sr
.top
, r
->start
.col
, r
->end
.col
,
2128 GNM_STYLE_BORDER_BOTTOM
);
2130 g_free (sr_array_data
);
2131 return user
.conflicts
;
2135 * sheet_style_relocate:
2138 * Slide the styles from the origin region to the new position.
2141 sheet_style_relocate (GnmExprRelocateInfo
const *rinfo
)
2144 GnmStyleList
*styles
;
2146 g_return_if_fail (rinfo
!= NULL
);
2148 styles
= sheet_style_get_range (rinfo
->origin_sheet
, &rinfo
->origin
);
2150 sheet_style_set_range (rinfo
->origin_sheet
, &rinfo
->origin
,
2151 sheet_style_default (rinfo
->origin_sheet
));
2152 corner
.col
= rinfo
->origin
.start
.col
+ rinfo
->col_offset
;
2153 corner
.row
= rinfo
->origin
.start
.row
+ rinfo
->row_offset
;
2154 sheet_style_set_list (rinfo
->target_sheet
, &corner
, styles
, NULL
, NULL
);
2155 style_list_free (styles
);
2159 * sheet_style_insdel_colrow:
2162 * Insert of delete style columns/rows.
2164 * For the insert case, we stretch the preceding column/row into there space
2168 sheet_style_insdel_colrow (GnmExprRelocateInfo
const *rinfo
)
2170 GnmStyleList
*styles
= NULL
;
2175 g_return_if_fail (rinfo
!= NULL
);
2176 g_return_if_fail (rinfo
->origin_sheet
== rinfo
->target_sheet
);
2177 g_return_if_fail ((rinfo
->col_offset
== 0) != (rinfo
->row_offset
== 0));
2179 is_insert
= (rinfo
->col_offset
+ rinfo
->row_offset
> 0);
2180 sheet
= rinfo
->origin_sheet
;
2183 /* 1) copy col/row to the top/left of the region, and extend it */
2184 corner
= rinfo
->origin
.start
;
2185 if (rinfo
->col_offset
) {
2186 int col
= MAX (corner
.col
- 1, 0);
2191 range_init_cols (&r
, sheet
, col
, col
);
2192 styles
= sheet_style_get_range (sheet
, &r
);
2193 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2194 GnmStyleRegion
*sr
= ptr
->data
;
2195 sr
->range
.end
.col
= rinfo
->col_offset
- 1;
2198 int row
= MAX (corner
.row
- 1, 0);
2203 range_init_rows (&r
, sheet
, row
, row
);
2204 styles
= sheet_style_get_range (sheet
, &r
);
2205 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2206 GnmStyleRegion
*sr
= ptr
->data
;
2207 sr
->range
.end
.row
= rinfo
->row_offset
- 1;
2212 sheet_style_relocate (rinfo
);
2215 sheet_style_set_list (sheet
, &corner
, styles
, NULL
, NULL
);
2216 style_list_free (styles
);
2221 cb_style_extent (GnmStyle
*style
,
2222 int corner_col
, int corner_row
, int width
, int height
,
2223 GnmRange
const *apply_to
, gpointer user
)
2225 GnmRange
*res
= user
;
2226 if (gnm_style_visible_in_blank (style
)) {
2229 /* The given dimensions refer to the tile, not the area. */
2230 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2231 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2233 tmp
= corner_col
+width
-1;
2234 if (res
->end
.col
< tmp
)
2236 if (res
->start
.col
> corner_col
)
2237 res
->start
.col
= corner_col
;
2239 tmp
= corner_row
+height
-1;
2240 if (res
->end
.row
< tmp
)
2242 if (res
->start
.row
> corner_row
)
2243 res
->start
.row
= corner_row
;
2248 * sheet_style_get_extent:
2249 * @sheet: sheet to measure
2250 * @r: starting range and resulting range
2252 * A simple implementation that finds the smallest range containing all visible styles
2253 * and containing @res.
2256 sheet_style_get_extent (Sheet
const *sheet
, GnmRange
*res
)
2260 range_init_full_sheet (&r
, sheet
);
2261 foreach_tile (sheet
, &r
, cb_style_extent
, res
);
2264 struct cb_nondefault_extent
{
2266 GnmStyle
**col_defaults
;
2270 cb_nondefault_extent (GnmStyle
*style
,
2271 int corner_col
, int corner_row
, int width
, int height
,
2272 GnmRange
const *apply_to
, gpointer user_
)
2274 struct cb_nondefault_extent
*user
= user_
;
2275 GnmRange
*res
= user
->res
;
2278 for (i
= 0; i
< width
; i
++) {
2279 int col
= corner_col
+ i
;
2280 if (col
>= apply_to
->start
.col
&&
2281 col
<= apply_to
->end
.col
&&
2282 style
!= user
->col_defaults
[col
]) {
2283 int max_row
= MIN (corner_row
+ height
- 1,
2285 int min_row
= MAX (corner_row
, apply_to
->start
.row
);
2287 res
->start
.col
= MIN (col
, res
->start
.col
);
2288 res
->start
.row
= MIN (min_row
, res
->start
.row
);
2290 res
->end
.col
= MAX (col
, res
->end
.col
);
2291 res
->end
.row
= MAX (max_row
, res
->end
.row
);
2297 sheet_style_get_nondefault_extent (Sheet
const *sheet
, GnmRange
*extent
,
2298 const GnmRange
*src
, GnmStyle
**col_defaults
)
2300 struct cb_nondefault_extent user
;
2302 user
.col_defaults
= col_defaults
;
2303 foreach_tile (sheet
, src
, cb_nondefault_extent
, &user
);
2306 struct cb_is_default
{
2308 GnmStyle
**col_defaults
;
2312 cb_is_default (GnmStyle
*style
,
2313 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2314 int width
, G_GNUC_UNUSED
int height
,
2315 GnmRange
const *apply_to
, gpointer user_
)
2317 struct cb_is_default
*user
= user_
;
2320 /* The given "width" refers to the tile, not the area. */
2321 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2323 for (i
= 0; user
->res
&& i
< width
; i
++) {
2324 if (style
!= user
->col_defaults
[corner_col
+ i
])
2330 sheet_style_is_default (Sheet
const *sheet
, const GnmRange
*r
, GnmStyle
**col_defaults
)
2332 struct cb_is_default user
;
2335 user
.col_defaults
= col_defaults
;
2337 foreach_tile (sheet
, r
, cb_is_default
, &user
);
2342 struct cb_get_nondefault
{
2344 GnmStyle
**col_defaults
;
2348 cb_get_nondefault (GnmStyle
*style
,
2349 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2350 int width
, G_GNUC_UNUSED
int height
,
2351 GnmRange
const *apply_to
, gpointer user_
)
2353 struct cb_get_nondefault
*user
= user_
;
2356 /* The given dimensions refer to the tile, not the area. */
2357 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2358 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2360 for (i
= 0; i
< width
; i
++) {
2361 if (style
!= user
->col_defaults
[corner_col
+ i
]) {
2363 for (j
= 0; j
< height
; j
++)
2364 user
->res
[corner_row
+ j
] = 1;
2371 sheet_style_get_nondefault_rows (Sheet
const *sheet
, GnmStyle
**col_defaults
)
2373 struct cb_get_nondefault user
;
2376 range_init_full_sheet (&r
, sheet
);
2378 user
.res
= g_new0 (guint8
, gnm_sheet_get_max_rows (sheet
));
2379 user
.col_defaults
= col_defaults
;
2381 foreach_tile (sheet
, &r
, cb_get_nondefault
, &user
);
2386 struct cb_most_common
{
2393 cb_most_common (GnmStyle
*style
,
2394 int corner_col
, int corner_row
, int width
, int height
,
2395 GnmRange
const *apply_to
, gpointer user
)
2397 struct cb_most_common
*cmc
= user
;
2398 int *counts
= g_hash_table_lookup (cmc
->h
, style
);
2401 counts
= g_new0 (int, cmc
->l
);
2402 g_hash_table_insert (cmc
->h
, style
, counts
);
2405 /* The given dimensions refer to the tile, not the area. */
2406 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2407 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2410 for (i
= 0; i
< width
; i
++)
2411 counts
[corner_col
+ i
] += height
;
2413 for (i
= 0; i
< height
; i
++)
2414 counts
[corner_row
+ i
] += width
;
2418 * sheet_style_most_common:
2419 * @sheet: sheet to inspect
2420 * @is_col: if %TRUE, look for common styles in columns; if FALSE, look in rows.
2422 * Returns: an array of styles describing the most common styles, one per column
2426 sheet_style_most_common (Sheet
const *sheet
, gboolean is_col
)
2429 struct cb_most_common cmc
;
2432 GHashTableIter iter
;
2433 gpointer key
, value
;
2435 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2437 range_init_full_sheet (&r
, sheet
);
2438 cmc
.h
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, g_free
);
2439 cmc
.l
= colrow_max (is_col
, sheet
);
2440 cmc
.is_col
= is_col
;
2441 foreach_tile (sheet
, &r
, cb_most_common
, &cmc
);
2443 max
= g_new0 (int, cmc
.l
);
2444 res
= g_new0 (GnmStyle
*, cmc
.l
);
2445 g_hash_table_iter_init (&iter
, cmc
.h
);
2446 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
2447 int *counts
= value
;
2448 GnmStyle
*style
= key
;
2450 for (j
= 0; j
< cmc
.l
; j
++) {
2451 /* FIXME: we really ought to break ties in a
2452 consistent way that does not depend on hash
2454 if (counts
[j
] > max
[j
]) {
2460 g_hash_table_destroy (cmc
.h
);
2466 /****************************************************************************/
2469 * gnm_style_region_new:
2473 * Returns: (transfer full): the newly allocated #GnmStyleRegion.
2476 gnm_style_region_new (GnmRange
const *range
, GnmStyle
*style
)
2480 sr
= g_new (GnmStyleRegion
, 1);
2483 gnm_style_ref (style
);
2489 gnm_style_region_free (GnmStyleRegion
*sr
)
2491 g_return_if_fail (sr
!= NULL
);
2493 gnm_style_unref (sr
->style
);
2498 static GnmStyleRegion
*
2499 gnm_style_region_copy (GnmStyleRegion
*sr
)
2501 GnmStyleRegion
*res
= g_new (GnmStyleRegion
, 1);
2503 gnm_style_ref (sr
->style
);
2508 gnm_style_region_get_type (void)
2513 t
= g_boxed_type_register_static ("GnmStyleRegion",
2514 (GBoxedCopyFunc
)gnm_style_region_copy
,
2515 (GBoxedFreeFunc
)gnm_style_region_free
);
2522 debug_style_list (void)
2524 static int debug
= -1;
2526 debug
= gnm_debug_flag ("style-list");
2532 GHashTable
*by_tl
, *by_br
;
2534 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
);
2535 gboolean (*style_filter
) (GnmStyle
const *style
);
2536 GnmSheetSize
const *sheet_size
;
2540 merge_ranges (GnmRange
*a
, GnmRange
const *b
)
2542 if (a
->start
.row
== b
->start
.row
&&
2543 a
->end
.row
== b
->end
.row
&&
2544 a
->end
.col
+ 1 == b
->start
.col
) {
2545 /* "a" is just left of "b". */
2546 a
->end
.col
= b
->end
.col
;
2550 if (a
->start
.col
== b
->start
.col
&&
2551 a
->end
.col
== b
->end
.col
&&
2552 a
->end
.row
+ 1 == b
->start
.row
) {
2553 /* "a" is just on top of "b". */
2554 a
->end
.row
= b
->end
.row
;
2563 try_merge_pair (ISL
*data
, unsigned ui1
, unsigned ui2
)
2568 if (ui1
>= data
->accum
->len
|| ui2
>= data
->accum
->len
)
2571 a
= g_ptr_array_index (data
->accum
, ui1
);
2572 b
= g_ptr_array_index (data
->accum
, ui2
);
2574 if (!data
->style_equal (a
->style
, b
->style
))
2577 if (!merge_ranges (&a
->range
, &b
->range
))
2580 gnm_style_region_free (b
);
2581 g_ptr_array_remove_index (data
->accum
, ui2
);
2587 cb_style_list_add_node (GnmStyle
*style
,
2588 int corner_col
, int corner_row
, int width
, int height
,
2589 GnmRange
const *apply_to
, gpointer user_
)
2592 GnmSheetSize
const *ss
= data
->sheet_size
;
2596 /* Can this even happen? */
2597 if (corner_col
>= ss
->max_cols
|| corner_row
>= ss
->max_rows
)
2600 if (data
->style_filter
&& !data
->style_filter (style
))
2603 range
.start
.col
= corner_col
;
2604 range
.start
.row
= corner_row
;
2605 range
.end
.col
= MIN (corner_col
+ width
- 1, ss
->max_cols
- 1);
2606 range
.end
.row
= MIN (corner_row
+ height
- 1, ss
->max_rows
- 1);
2609 range
.start
.col
-= apply_to
->start
.col
;
2610 if (range
.start
.col
< 0)
2611 range
.start
.col
= 0;
2612 range
.start
.row
-= apply_to
->start
.row
;
2613 if (range
.start
.row
< 0)
2614 range
.start
.row
= 0;
2616 if (range
.end
.col
> apply_to
->end
.col
)
2617 range
.end
.col
= apply_to
->end
.col
;
2618 range
.end
.col
-= apply_to
->start
.col
;
2619 if (range
.end
.row
> apply_to
->end
.row
)
2620 range
.end
.row
= apply_to
->end
.row
;
2621 range
.end
.row
-= apply_to
->start
.row
;
2624 data
->area
+= (guint64
)range_width (&range
) * range_height (&range
);
2626 sr
= gnm_style_region_new (&range
, style
);
2627 g_ptr_array_add (data
->accum
, sr
);
2629 while (try_merge_pair (data
, data
->accum
->len
- 2, data
->accum
->len
- 1))
2634 verify_hashes (ISL
*data
)
2636 GHashTable
*by_tl
= data
->by_tl
;
2637 GHashTable
*by_br
= data
->by_br
;
2641 g_return_if_fail (g_hash_table_size (by_tl
) == data
->accum
->len
);
2642 g_return_if_fail (g_hash_table_size (by_br
) == data
->accum
->len
);
2644 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2645 GnmStyleRegion
*sr
= g_ptr_array_index (data
->accum
, ui
);
2646 g_return_if_fail (g_hash_table_lookup (by_tl
, &sr
->range
.start
) == sr
);
2647 g_return_if_fail (g_hash_table_lookup (by_br
, &sr
->range
.end
) == sr
);
2648 area
+= range_height (&sr
->range
) *
2649 (guint64
)range_width (&sr
->range
);
2652 g_return_if_fail (area
== data
->area
);
2656 merge_vertical_stripes (ISL
*data
)
2659 GHashTable
*by_tl
= data
->by_tl
;
2660 GHashTable
*by_br
= data
->by_br
;
2661 gboolean debug
= debug_style_list ();
2662 gboolean paranoid
= debug
;
2664 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2665 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2668 GSList
*Bs
= NULL
, *l
;
2669 gboolean fail
= FALSE
;
2671 /* We're looking for the setup below and extend Bs down */
2672 /* taking over part of C which is then extended to */
2673 /* include all of A. */
2677 /* +---------+ B1 | B2 | */
2679 /* +---------+----+---------+ */
2681 /* +------------------------+ */
2683 cr
.col
= a
->range
.start
.col
;
2684 cr
.row
= a
->range
.end
.row
+ 1;
2685 c
= g_hash_table_lookup (by_tl
, &cr
);
2686 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2689 cr
.col
= c
->range
.end
.col
;
2690 cr
.row
= a
->range
.end
.row
;
2691 while (cr
.col
> a
->range
.end
.col
) {
2692 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2693 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2697 Bs
= g_slist_prepend (Bs
, b
);
2698 cr
.col
= b
->range
.start
.col
- 1;
2700 if (fail
|| cr
.col
!= a
->range
.end
.col
) {
2706 g_printerr ("Vertical stripe merge:\n");
2707 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2708 for (l
= Bs
; l
; l
= l
-> next
) {
2709 GnmStyleRegion
*b
= l
->data
;
2710 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2712 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2715 g_hash_table_remove (by_tl
, &a
->range
.start
);
2716 g_hash_table_remove (by_br
, &a
->range
.end
);
2717 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2720 g_hash_table_remove (by_tl
, &c
->range
.start
);
2721 g_hash_table_remove (by_br
, &c
->range
.end
);
2722 c
->range
.start
.row
= a
->range
.start
.row
;
2723 c
->range
.end
.col
= a
->range
.end
.col
;
2724 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2725 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2727 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2729 for (l
= Bs
; l
; l
= l
-> next
) {
2730 GnmStyleRegion
*b
= l
->data
;
2731 g_hash_table_remove (by_br
, &b
->range
.end
);
2732 b
->range
.end
.row
= c
->range
.end
.row
;
2733 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2735 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2740 gnm_style_region_free (a
);
2743 if (paranoid
) verify_hashes (data
);
2748 merge_horizontal_stripes (ISL
*data
)
2751 GHashTable
*by_tl
= data
->by_tl
;
2752 GHashTable
*by_br
= data
->by_br
;
2753 gboolean debug
= debug_style_list ();
2754 gboolean paranoid
= debug
;
2756 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2757 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2760 GSList
*Bs
= NULL
, *l
;
2761 gboolean fail
= FALSE
;
2763 /* We're looking for the setup below and extend Bs right */
2764 /* taking over part of C which is then extended to */
2765 /* include all of A. */
2769 /* +----+-----+ | */
2771 /* +--+-------+ | */
2777 /* +-------+-----+ */
2779 cr
.col
= a
->range
.end
.col
+ 1;
2780 cr
.row
= a
->range
.start
.row
;
2781 c
= g_hash_table_lookup (by_tl
, &cr
);
2782 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2785 cr
.col
= a
->range
.end
.col
;
2786 cr
.row
= c
->range
.end
.row
;
2787 while (cr
.row
> a
->range
.end
.row
) {
2788 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2789 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2793 Bs
= g_slist_prepend (Bs
, b
);
2794 cr
.row
= b
->range
.start
.row
- 1;
2796 if (fail
|| cr
.row
!= a
->range
.end
.row
) {
2802 g_printerr ("Horizontal stripe merge:\n");
2803 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2804 for (l
= Bs
; l
; l
= l
-> next
) {
2805 GnmStyleRegion
*b
= l
->data
;
2806 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2808 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2811 g_hash_table_remove (by_tl
, &a
->range
.start
);
2812 g_hash_table_remove (by_br
, &a
->range
.end
);
2813 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2816 g_hash_table_remove (by_tl
, &c
->range
.start
);
2817 g_hash_table_remove (by_br
, &c
->range
.end
);
2818 c
->range
.start
.col
= a
->range
.start
.col
;
2819 c
->range
.end
.row
= a
->range
.end
.row
;
2820 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2821 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2823 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2825 for (l
= Bs
; l
; l
= l
-> next
) {
2826 GnmStyleRegion
*b
= l
->data
;
2827 g_hash_table_remove (by_br
, &b
->range
.end
);
2828 b
->range
.end
.col
= c
->range
.end
.col
;
2829 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2831 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2836 gnm_style_region_free (a
);
2839 if (paranoid
) verify_hashes (data
);
2844 by_col_row (GnmStyleRegion
**a
, GnmStyleRegion
**b
)
2848 d
= (*a
)->range
.start
.col
- (*b
)->range
.start
.col
;
2852 d
= (*a
)->range
.start
.row
- (*b
)->range
.start
.row
;
2856 static GnmStyleList
*
2857 internal_style_list (Sheet
const *sheet
, GnmRange
const *r
,
2858 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
),
2859 gboolean (*style_filter
) (GnmStyle
const *style
))
2861 GnmRange full_sheet
;
2863 GnmStyleList
*res
= NULL
;
2864 unsigned ui
, prelen
;
2865 gboolean paranoid
= FALSE
;
2868 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2871 /* This can happen if the last row or column is deleted. */
2872 if (!range_valid (r
))
2875 r
= range_init_full_sheet (&full_sheet
, sheet
);
2877 data
.accum
= g_ptr_array_new ();
2878 data
.by_tl
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2879 (GEqualFunc
)gnm_cellpos_equal
);
2880 data
.by_br
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2881 (GEqualFunc
)gnm_cellpos_equal
);
2883 data
.style_equal
= style_equal
;
2884 data
.style_filter
= style_filter
;
2885 data
.sheet_size
= gnm_sheet_get_size (sheet
);
2887 foreach_tile (sheet
, r
, cb_style_list_add_node
, &data
);
2889 sheet_area
= (guint64
)range_height (r
) * range_width (r
);
2890 if (data
.style_filter
? (data
.area
> sheet_area
) : (data
.area
!= sheet_area
))
2891 g_warning ("Strange size issue in internal_style_list");
2894 * Simple, fast optimization first. For the file underlying
2895 * bug 699045 this brings down 332688 entries to just 86.
2897 if (data
.accum
->len
>= 2) {
2898 g_ptr_array_sort (data
.accum
, (GCompareFunc
)by_col_row
);
2899 for (ui
= data
.accum
->len
- 1; ui
> 0; ui
--) {
2900 try_merge_pair (&data
, ui
- 1, ui
);
2904 /* Populate hashes. */
2905 for (ui
= 0; ui
< data
.accum
->len
; ui
++) {
2906 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2907 g_hash_table_insert (data
.by_tl
, &sr
->range
.start
, sr
);
2908 g_hash_table_insert (data
.by_br
, &sr
->range
.end
, sr
);
2911 if (paranoid
) verify_hashes (&data
);
2914 prelen
= data
.accum
->len
;
2915 merge_vertical_stripes (&data
);
2916 merge_horizontal_stripes (&data
);
2917 } while (prelen
> data
.accum
->len
);
2919 /* Always verify once. */
2920 verify_hashes (&data
);
2922 if (debug_style_list ())
2923 g_printerr ("Total of %d ranges:\n", data
.accum
->len
);
2924 for (ui
= data
.accum
->len
; ui
-- > 0; ) {
2925 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2926 if (debug_style_list ())
2927 g_printerr (" %s %p\n",
2928 range_as_string (&sr
->range
),
2930 res
= g_slist_prepend (res
, sr
);
2933 g_ptr_array_free (data
.accum
, TRUE
);
2934 g_hash_table_destroy (data
.by_tl
);
2935 g_hash_table_destroy (data
.by_br
);
2940 * sheet_style_get_range:
2941 * @sheet: the sheet in which to find styles
2942 * @r: optional range to scan
2944 * Get a list of rectangles and their associated styles.
2945 * Caller is responsible for freeing. Note that when a range is given,
2946 * the resulting ranges are relative to the input range.
2948 * Returns: (transfer full):
2951 sheet_style_get_range (Sheet
const *sheet
, GnmRange
const *r
)
2953 return internal_style_list (sheet
, r
,
2959 style_conditions_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2961 return gnm_style_get_conditions (a
) == gnm_style_get_conditions (b
);
2965 style_conditions_filter (GnmStyle
const *style
)
2967 return gnm_style_get_conditions (style
) != NULL
;
2971 * sheet_style_collect_conditions:
2975 * Returns: (transfer full): a list of areas with conditionals, Caller is
2976 * responsible for freeing.
2979 sheet_style_collect_conditions (Sheet
const *sheet
, GnmRange
const *r
)
2981 return internal_style_list (sheet
, r
,
2982 style_conditions_equal
,
2983 style_conditions_filter
);
2988 style_hlink_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2990 return gnm_style_get_hlink (a
) == gnm_style_get_hlink (b
);
2994 style_hlink_filter (GnmStyle
const *style
)
2996 return gnm_style_get_hlink (style
) != NULL
;
3000 * sheet_style_collect_hlinks:
3004 * Returns: (transfer full): a list of areas with hyperlinks, Caller is
3005 * responsible for freeing.
3008 sheet_style_collect_hlinks (Sheet
const *sheet
, GnmRange
const *r
)
3010 return internal_style_list (sheet
, r
,
3012 style_hlink_filter
);
3017 style_validation_equal (GnmStyle
const *a
, GnmStyle
const *b
)
3019 return gnm_style_get_validation (a
) == gnm_style_get_validation (b
) &&
3020 gnm_style_get_input_msg (a
) == gnm_style_get_input_msg (b
);
3024 style_validation_filter (GnmStyle
const *style
)
3026 return (gnm_style_get_validation (style
) != NULL
||
3027 gnm_style_get_input_msg (style
) != NULL
);
3031 * sheet_style_collect_validations:
3032 * @sheet: the to trawl
3033 * @r: (allow-none): range to restrict to
3035 * Returns: (transfer full): a list of areas with validation or input
3039 sheet_style_collect_validations (Sheet
const *sheet
, GnmRange
const *r
)
3041 return internal_style_list (sheet
, r
,
3042 style_validation_equal
,
3043 style_validation_filter
);
3047 * sheet_style_set_list:
3049 * @corner: The top-left corner (in LTR mode)
3051 * @range_modify: (scope call):
3054 * Overwrites the styles of the ranges given by @corner with the content of
3055 * @list. Optionally transposing the ranges
3058 sheet_style_set_list (Sheet
*sheet
, GnmCellPos
const *corner
,
3059 GnmStyleList
const *list
,
3060 sheet_style_set_list_cb_t range_modify
,
3063 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_SIMPLE
;
3064 GnmStyleList
const *l
;
3066 g_return_val_if_fail (IS_SHEET (sheet
), spanflags
);
3068 /* Sluggish but simple implementation for now */
3069 for (l
= list
; l
; l
= l
->next
) {
3070 GnmStyleRegion
const *sr
= l
->data
;
3071 GnmRange r
= sr
->range
;
3073 range_translate (&r
, sheet
, +corner
->col
, +corner
->row
);
3075 range_modify (&r
, sheet
, data
);
3077 gnm_style_ref (sr
->style
);
3078 sheet_style_set_range (sheet
, &r
, sr
->style
);
3079 spanflags
|= gnm_style_required_spanflags (sr
->style
);
3086 * @l: the list to free
3088 * Free up the ressources in the style list. Including unreferencing the
3092 style_list_free (GnmStyleList
*list
)
3094 g_slist_free_full (list
, (GDestroyNotify
)gnm_style_region_free
);
3098 * style_list_get_style:
3103 * Attempts to find the style associated with the @pos offset within the 0,0
3105 * The resulting style does not have its reference count bumped.
3108 style_list_get_style (GnmStyleList
const *list
, int col
, int row
)
3110 GnmStyleList
const *l
;
3112 for (l
= list
; l
; l
= l
->next
) {
3113 GnmStyleRegion
const *sr
= l
->data
;
3114 GnmRange
const *r
= &sr
->range
;
3115 if (range_contains (r
, col
, row
))
3122 cb_find_link (GnmStyle
*style
,
3123 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
3124 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
3125 G_GNUC_UNUSED GnmRange
const *apply_to
, gpointer user
)
3127 GnmHLink
**plink
= user
;
3129 *plink
= gnm_style_get_hlink (style
);
3133 * sheet_style_region_contains_link:
3137 * Utility routine that checks to see if a region contains at least 1 hyper link
3138 * and returns the 1st one it finds.
3140 * Returns: (transfer none): the found #GmHLink if any.
3143 sheet_style_region_contains_link (Sheet
const *sheet
, GnmRange
const *r
)
3145 GnmHLink
*res
= NULL
;
3147 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3148 g_return_val_if_fail (r
!= NULL
, NULL
);
3150 foreach_tile (sheet
, r
, cb_find_link
, &res
);
3155 * sheet_style_foreach:
3157 * @func: (scope call): callback
3158 * @user_data: user data.
3160 * Executes @func for each style in the sheet.
3163 sheet_style_foreach (Sheet
const *sheet
, GFunc func
, gpointer user_data
)
3167 g_return_if_fail (IS_SHEET (sheet
));
3168 g_return_if_fail (sheet
->style_data
!= NULL
);
3170 styles
= sh_all_styles (sheet
->style_data
->style_hash
);
3171 styles
= g_slist_sort (styles
, (GCompareFunc
)gnm_style_cmp
);
3172 g_slist_foreach (styles
, func
, user_data
);
3173 g_slist_free (styles
);
3177 * sheet_style_range_foreach:
3179 * @r: optional range
3180 * @func: (scope call): callback.
3181 * @user_data: user data
3185 sheet_style_range_foreach (Sheet
const *sheet
, GnmRange
const *r
,
3186 GHFunc func
, gpointer user_data
)
3188 GnmStyleList
*styles
, *l
;
3190 styles
= sheet_style_get_range (sheet
, r
);
3192 for (l
= styles
; l
; l
= l
->next
) {
3193 GnmStyleRegion
*sr
= l
->data
;
3195 sr
->range
.start
.col
+= r
->start
.col
;
3196 sr
->range
.start
.row
+= r
->start
.row
;
3197 sr
->range
.end
.col
+= r
->start
.col
;
3198 sr
->range
.end
.row
+= r
->start
.row
;
3200 func (NULL
, sr
, user_data
);
3201 gnm_style_region_free (sr
);
3204 g_slist_free (styles
);
3207 /* ------------------------------------------------------------------------- */
3210 cell_tile_dump (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3214 int const w
= tile_widths
[level
];
3215 int const h
= tile_heights
[level
];
3217 const char *indent
= "";
3219 type
= (*tile
)->type
;
3223 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3224 data
->ss
->max_cols
- 1),
3225 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3226 data
->ss
->max_rows
- 1));
3228 g_printerr ("%s%s: type %s\n",
3230 range_as_string (&rng
),
3231 tile_type_str
[type
]);
3233 if (type
== TILE_PTR_MATRIX
) {
3236 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3237 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3238 int c
= i
% TILE_SIZE_COL
;
3239 int r
= i
/ TILE_SIZE_COL
;
3240 cell_tile_dump (subtile
, level
- 1, data
,
3248 for (i
= 0; i
< tile_size
[type
]; i
++) {
3249 g_printerr ("%s: %d %p\n",
3252 (*tile
)->style_any
.style
[i
]);
3258 /* ------------------------------------------------------------------------- */
3261 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3265 int const w
= tile_widths
[level
];
3266 int const h
= tile_heights
[level
];
3271 type
= (*tile
)->type
;
3272 if (type
== TILE_SIMPLE
)
3277 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3278 data
->ss
->max_cols
- 1),
3279 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3280 data
->ss
->max_rows
- 1));
3285 if (!tile_is_uniform (*tile
))
3291 case TILE_PTR_MATRIX
: {
3292 gboolean all_simple
= TRUE
;
3295 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3296 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3297 if (data
->recursion
) {
3298 int c
= i
% TILE_SIZE_COL
;
3299 int r
= i
/ TILE_SIZE_COL
;
3300 cell_tile_optimize (subtile
, level
- 1, data
,
3304 if ((*subtile
)->type
!= TILE_SIMPLE
)
3310 res
= cell_tile_style_new (NULL
, TILE_MATRIX
);
3311 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3312 CellTile
*subtile
= (*tile
)->ptr_matrix
.ptr
[i
];
3313 GnmStyle
*st
= subtile
->style_simple
.style
[0];
3314 gnm_style_link (st
);
3315 res
->style_matrix
.style
[i
] = st
;
3318 if (debug_style_optimize
)
3319 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3320 range_as_string (&rng
),
3321 range_width (&rng
), range_height (&rng
),
3322 tile_type_str
[(*tile
)->type
],
3323 tile_type_str
[res
->type
]);
3324 cell_tile_dtor (*tile
);
3331 gboolean csame
= TRUE
;
3332 gboolean rsame
= TRUE
;
3335 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
3336 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
3338 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3339 (*tile
)->style_matrix
.style
[i
])) {
3345 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3346 (*tile
)->style_matrix
.style
[ c
])) {
3365 g_assert_not_reached ();
3368 if (debug_style_optimize
)
3369 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3370 range_as_string (&rng
),
3371 range_width (&rng
), range_height (&rng
),
3372 tile_type_str
[(*tile
)->type
],
3373 tile_type_str
[type
]);
3375 res
= cell_tile_style_new (NULL
, type
);
3378 res
->style_simple
.style
[0] = (*tile
)->style_any
.style
[0];
3381 for (i
= 0; i
< TILE_SIZE_ROW
; i
++)
3382 res
->style_row
.style
[i
] =
3383 (*tile
)->style_matrix
.style
[i
* TILE_SIZE_COL
];
3386 for (i
= 0; i
< TILE_SIZE_COL
; i
++)
3387 res
->style_col
.style
[i
] =
3388 (*tile
)->style_matrix
.style
[i
];
3391 g_assert_not_reached ();
3394 for (i
= 0; i
< tile_size
[type
]; i
++)
3395 gnm_style_link (res
->style_any
.style
[i
]);
3397 cell_tile_dtor (*tile
);
3402 sample_styles (Sheet
*sheet
)
3404 GnmSheetSize
const *ss
= gnm_sheet_get_size (sheet
);
3410 GnmStyle
const *mstyle
= sheet_style_get (sheet
, c
, r
);
3411 if (res
== NULL
|| !gnm_style_eq (mstyle
, res
->data
)) {
3412 gnm_style_ref (mstyle
);
3413 res
= g_slist_prepend (res
, GINT_TO_POINTER (c
));
3414 res
= g_slist_prepend (res
, GINT_TO_POINTER (r
));
3415 res
= g_slist_prepend (res
, (gpointer
)mstyle
);
3419 if (c
>= ss
->max_cols
) {
3422 if (r
>= ss
->max_rows
)
3427 return g_slist_reverse (res
);
3431 verify_styles (GSList
*pre
, GSList
*post
)
3433 GSList
*lpre
, *lpost
;
3434 gboolean silent
= FALSE
, bad
= FALSE
;
3436 for (lpre
= pre
, lpost
= post
;
3438 lpre
= (lpre
? lpre
->next
->next
->next
: NULL
),
3439 lpost
= (lpost
? lpost
->next
->next
->next
: NULL
)) {
3440 int cpre
= lpre
? GPOINTER_TO_INT (lpre
->data
) : -1;
3441 int rpre
= lpre
? GPOINTER_TO_INT (lpre
->next
->data
) : -1;
3442 GnmStyle
const *spre
= lpre
? lpre
->next
->next
->data
: NULL
;
3443 int cpost
= lpost
? GPOINTER_TO_INT (lpost
->data
) : -1;
3444 int rpost
= lpost
? GPOINTER_TO_INT (lpost
->next
->data
) : -1;
3445 GnmStyle
const *spost
= lpost
? lpost
->next
->next
->data
: NULL
;
3448 if (!spre
|| !spost
) {
3450 g_warning ("Style optimizer failure at end!");
3452 } else if (cpre
!= cpost
|| rpre
!= rpost
) {
3454 g_warning ("Style optimizer position conflict at %s!",
3455 cell_coord_name (cpre
, rpre
));
3457 } else if (!gnm_style_eq (spre
, spost
)) {
3459 g_warning ("Style optimizer failure at %s!",
3460 cell_coord_name (cpre
, rpre
));
3464 if (spre
) gnm_style_unref (spre
);
3465 if (spost
) gnm_style_unref (spost
);
3469 g_slist_free (post
);
3475 sheet_style_optimize (Sheet
*sheet
)
3477 CellTileOptimize data
;
3481 g_return_if_fail (IS_SHEET (sheet
));
3483 if (gnm_debug_flag ("no-style-optimize"))
3486 sheet_colrow_optimize (sheet
);
3488 data
.ss
= gnm_sheet_get_size (sheet
);
3489 data
.recursion
= TRUE
;
3491 if (debug_style_optimize
) {
3492 g_printerr ("Optimizing %s\n", sheet
->name_unquoted
);
3493 cell_tile_dump (&sheet
->style_data
->styles
,
3494 sheet
->tile_top_level
, &data
,
3498 verify
= gnm_debug_flag ("style-optimize-verify");
3499 pre
= verify
? sample_styles (sheet
) : NULL
;
3501 cell_tile_optimize (&sheet
->style_data
->styles
,
3502 sheet
->tile_top_level
, &data
,
3505 if (debug_style_optimize
)
3506 g_printerr ("Optimizing %s...done\n", sheet
->name_unquoted
);
3509 GSList
*post
= sample_styles (sheet
);
3510 verify_styles (pre
, post
);