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
187 * Looks up a style from the sheets collection. Linking if necessary.
188 * ABSORBS the reference and adds a link.
190 * Returns: (transfer full): the new style.
193 sheet_style_find (Sheet
const *sheet
, GnmStyle
*s
)
196 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
198 gnm_style_link (res
);
203 s
= gnm_style_link_sheet (s
, (Sheet
*)sheet
);
205 /* Retry the lookup in case "s" changed. See #585178. */
206 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
208 gnm_style_link (res
);
210 * We are abandoning the linking here. We cannot use
211 * gnm_style_unlink as that would call sheet_style_unlink
212 * and thus remove "res" from the hash.
214 gnm_style_abandon_link (s
);
220 sh_insert (sheet
->style_data
->style_hash
, s
);
224 /* Place holder until I merge in the new styles too */
226 pstyle_set_border (GnmStyle
*st
, GnmBorder
*border
,
227 GnmStyleBorderLocation side
)
229 gnm_style_set_border (st
,
230 GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (side
),
231 gnm_style_border_ref (border
));
234 /* Amortize the cost of applying a partial style over a large region
235 * by caching and rereferencing the merged result for repeated styles.
245 rstyle_ctor_style (ReplacementStyle
*res
, GnmStyle
*new_style
, Sheet
*sheet
)
248 res
->new_style
= sheet_style_find (sheet
, new_style
);
254 rstyle_ctor_pstyle (ReplacementStyle
*res
, GnmStyle
*pstyle
, Sheet
*sheet
)
257 res
->new_style
= NULL
;
258 res
->pstyle
= pstyle
;
259 res
->cache
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
263 cb_style_unlink (gpointer key
, gpointer value
, G_GNUC_UNUSED gpointer user_data
)
265 gnm_style_unlink ((GnmStyle
*)key
);
266 gnm_style_unlink ((GnmStyle
*)value
);
270 rstyle_dtor (ReplacementStyle
*rs
)
272 if (rs
->cache
!= NULL
) {
273 g_hash_table_foreach (rs
->cache
, cb_style_unlink
, NULL
);
274 g_hash_table_destroy (rs
->cache
);
277 if (rs
->new_style
!= NULL
) {
278 gnm_style_unlink (rs
->new_style
);
279 rs
->new_style
= NULL
;
281 if (rs
->pstyle
!= NULL
) {
282 gnm_style_unref (rs
->pstyle
);
288 * rstyle_apply: Utility routine that is at the core of applying partial
289 * styles or storing complete styles. It will eventually be smarter
290 * and will maintain the cache of styles associated with each sheet
293 rstyle_apply (GnmStyle
**old
, ReplacementStyle
*rs
, GnmRange
const *r
)
296 g_return_if_fail (old
!= NULL
);
297 g_return_if_fail (rs
!= NULL
);
299 if (rs
->pstyle
!= NULL
) {
300 /* Cache the merged styles keeping a reference to the originals
301 * just in case all instances change.
303 s
= g_hash_table_lookup (rs
->cache
, *old
);
305 GnmStyle
*tmp
= gnm_style_new_merged (*old
, rs
->pstyle
);
306 s
= sheet_style_find (rs
->sheet
, tmp
);
307 gnm_style_link (*old
);
308 g_hash_table_insert (rs
->cache
, *old
, s
);
315 gnm_style_unlink_dependents (*old
, r
);
316 gnm_style_unlink (*old
);
319 gnm_style_link_dependents (s
, r
);
327 sheet_style_clear_style_dependents (Sheet
*sheet
, GnmRange
const *r
)
329 GSList
*styles
= sh_all_styles (sheet
->style_data
->style_hash
);
330 g_slist_foreach (styles
,
331 (GFunc
)gnm_style_unlink_dependents
,
333 g_slist_free (styles
);
337 /****************************************************************************/
339 /* If you change this, change the tile_{widths,heights} here
340 * and GNM_MAX_COLS and GNM_MAX_ROWS in gnumeric.h */
341 #define TILE_TOP_LEVEL 6
343 #define TILE_SIZE_COL 8
344 #define TILE_SIZE_ROW 16
354 static int const tile_size
[/*type*/] = {
356 TILE_SIZE_COL
, /* TILE_COL */
357 TILE_SIZE_ROW
, /* TILE_ROW */
358 TILE_SIZE_COL
* TILE_SIZE_ROW
/* TILE_MATRIX */
360 static int const tile_col_count
[/*type*/] = {
362 TILE_SIZE_COL
, /* TILE_COL */
364 TILE_SIZE_COL
, /* TILE_MATRIX */
365 TILE_SIZE_COL
/* TILE_PTR_MATRIX */
367 static int const tile_row_count
[/*type*/] = {
370 TILE_SIZE_ROW
, /* TILE_ROW */
371 TILE_SIZE_ROW
, /* TILE_MATRIX */
372 TILE_SIZE_ROW
/* TILE_PTR_MATRIX */
374 static const char * const tile_type_str
[/*type*/] = {
375 "simple", "col", "row", "matrix", "ptr-matrix"
377 static int const tile_widths
[/*level*/] = {
380 TILE_SIZE_COL
* TILE_SIZE_COL
,
381 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
382 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
,
384 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
385 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
387 static int const tile_heights
[/*level*/] = {
390 TILE_SIZE_ROW
* TILE_SIZE_ROW
,
391 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
392 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
,
394 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
395 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
399 CellTileType
const type
;
401 } CellTileStyleSimple
;
403 CellTileType
const type
;
404 GnmStyle
*style
[TILE_SIZE_COL
];
407 CellTileType
const type
;
408 GnmStyle
*style
[TILE_SIZE_ROW
];
411 CellTileType
const type
;
412 GnmStyle
*style
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
413 } CellTileStyleMatrix
;
415 CellTileType
const type
;
416 CellTile
*ptr
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
420 CellTileType
const type
;
421 CellTileStyleSimple style_any
;
422 CellTileStyleSimple style_simple
;
423 CellTileStyleCol style_col
;
424 CellTileStyleRow style_row
;
425 CellTileStyleMatrix style_matrix
;
426 CellTilePtrMatrix ptr_matrix
;
429 static int active_sheet_count
;
431 static GOMemChunk
*tile_pools
[5];
432 #define CHUNK_ALLOC(T,ctt) ((T*)go_mem_chunk_alloc (tile_pools[(ctt)]))
433 #define CHUNK_FREE(ctt,v) go_mem_chunk_free (tile_pools[(ctt)], (v))
435 static const size_t tile_type_sizeof
[5] = {
436 sizeof (CellTileStyleSimple
),
437 sizeof (CellTileStyleCol
),
438 sizeof (CellTileStyleRow
),
439 sizeof (CellTileStyleMatrix
),
440 sizeof (CellTilePtrMatrix
)
442 static int tile_allocations
= 0;
444 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_slice_alloc (tile_type_sizeof[(ctt)]))
445 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_slice_free1 (tile_type_sizeof[(ctt)], (v)))
447 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_malloc (tile_type_sizeof[(ctt)]))
448 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_free ((v)))
454 * Destroy a CellTile (recursively if needed). This will unlink all the
455 * styles in it. We do _not_ unlink style dependents here. That is done
456 * only in rstyle_apply.
459 cell_tile_dtor (CellTile
*tile
)
463 g_return_if_fail (tile
!= NULL
);
466 if (t
== TILE_PTR_MATRIX
) {
467 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
469 cell_tile_dtor (tile
->ptr_matrix
.ptr
[i
]);
470 tile
->ptr_matrix
.ptr
[i
] = NULL
;
472 } else if (TILE_SIMPLE
<= t
&& t
<= TILE_MATRIX
) {
473 int i
= tile_size
[t
];
475 gnm_style_unlink (tile
->style_any
.style
[i
]);
476 tile
->style_any
.style
[i
] = NULL
;
479 g_return_if_fail (FALSE
); /* don't free anything */
482 *((CellTileType
*)&(tile
->type
)) = TILE_UNDEFINED
; /* poison it */
483 CHUNK_FREE (t
, tile
);
487 cell_tile_style_new (GnmStyle
*style
, CellTileType t
)
489 CellTile
*res
= CHUNK_ALLOC (CellTile
, t
);
490 *((CellTileType
*)&(res
->type
)) = t
;
493 int i
= tile_size
[t
];
494 gnm_style_link_multiple (style
, i
);
496 res
->style_any
.style
[i
] = style
;
503 cell_tile_ptr_matrix_new (CellTile
*t
)
505 CellTilePtrMatrix
*res
;
507 g_return_val_if_fail (t
!= NULL
, NULL
);
508 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
509 TILE_MATRIX
>= t
->type
, NULL
);
511 res
= CHUNK_ALLOC (CellTilePtrMatrix
, TILE_PTR_MATRIX
);
512 *((CellTileType
*)&(res
->type
)) = TILE_PTR_MATRIX
;
515 * If we wanted to get fancy we could use self similarity to decrease
516 * the number of subtiles. However, this would increase the cost of
517 * applying changes later so I'm not sure it is worth the effort.
521 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
523 res
->ptr
[i
] = cell_tile_style_new (
524 t
->style_simple
.style
[0], TILE_SIMPLE
);
529 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
530 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
531 res
->ptr
[i
++] = cell_tile_style_new (
532 t
->style_col
.style
[c
], TILE_SIMPLE
);
537 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
538 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
539 res
->ptr
[i
++] = cell_tile_style_new (
540 t
->style_row
.style
[r
], TILE_SIMPLE
);
544 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
546 res
->ptr
[i
] = cell_tile_style_new (
547 t
->style_matrix
.style
[i
], TILE_SIMPLE
);
553 return (CellTile
*)res
;
557 cell_tile_matrix_set (CellTile
*t
)
560 CellTileStyleMatrix
*res
;
562 g_return_val_if_fail (t
!= NULL
, NULL
);
563 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
564 TILE_MATRIX
>= t
->type
, NULL
);
566 if (t
->type
== TILE_MATRIX
)
569 res
= (CellTileStyleMatrix
*)cell_tile_style_new (NULL
, TILE_MATRIX
);
573 GnmStyle
*tmp
= t
->style_simple
.style
[0];
574 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
575 gnm_style_link_multiple (tmp
, i
);
583 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
)
584 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
585 gnm_style_link (res
->style
[i
++] =
586 t
->style_col
.style
[c
]);
592 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
) {
593 GnmStyle
*tmp
= t
->style_row
.style
[r
];
594 gnm_style_link_multiple (tmp
, TILE_SIZE_COL
);
595 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
596 res
->style
[i
++] = tmp
;
603 g_assert_not_reached();
608 return (CellTile
*)res
;
611 /****************************************************************************/
614 sheet_style_sanity_check (void)
619 for (c
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
620 g_assert (c
< G_MAXUINT
/ TILE_SIZE_COL
);
623 g_assert (c
>= GNM_MAX_COLS
);
625 for (r
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
626 g_assert (r
< G_MAXUINT
/ TILE_SIZE_COL
);
629 g_assert (r
>= GNM_MAX_ROWS
);
631 g_assert (G_N_ELEMENTS (tile_heights
) > TILE_TOP_LEVEL
+ 1);
633 g_assert (G_N_ELEMENTS (tile_widths
) > TILE_TOP_LEVEL
+ 1);
637 sheet_style_init_size (Sheet
*sheet
, int cols
, int rows
)
639 GnmStyle
*default_style
;
640 int lc
= 0, lr
= 0, w
= TILE_SIZE_COL
, h
= TILE_SIZE_ROW
;
650 sheet
->tile_top_level
= MAX (lc
, lr
);
652 if (active_sheet_count
++ == 0) {
654 tile_pools
[TILE_SIMPLE
] =
655 go_mem_chunk_new ("simple tile pool",
656 sizeof (CellTileStyleSimple
),
658 tile_pools
[TILE_COL
] =
659 go_mem_chunk_new ("column tile pool",
660 sizeof (CellTileStyleCol
),
662 tile_pools
[TILE_ROW
] =
663 go_mem_chunk_new ("row tile pool",
664 sizeof (CellTileStyleRow
),
666 tile_pools
[TILE_MATRIX
] =
667 go_mem_chunk_new ("matrix tile pool",
668 sizeof (CellTileStyleMatrix
),
669 MAX (16 * 1024 - 128,
670 100 * sizeof (CellTileStyleMatrix
)));
672 /* If this fails one day, just make two pools. */
673 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
674 tile_pools
[TILE_PTR_MATRIX
] = tile_pools
[TILE_MATRIX
];
678 sheet
->style_data
= g_new (GnmSheetStyleData
, 1);
679 sheet
->style_data
->style_hash
= sh_create ();
681 sheet
->style_data
->auto_pattern_color
= style_color_auto_pattern ();
683 default_style
= gnm_style_new_default ();
685 /* We can not do this, XL creates full page charts with background
686 * 'none' by default. Then displays that as white. */
687 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
688 gnm_style_set_back_color (default_style
,
689 gnm_color_new_rgb8 (0x50, 0x50, 0x50));
690 gnm_style_set_pattern (default_style
, 1);
693 sheet
->style_data
->default_style
=
694 sheet_style_find (sheet
, default_style
);
695 sheet
->style_data
->styles
=
696 cell_tile_style_new (sheet
->style_data
->default_style
,
701 sheet_style_init (Sheet
*sheet
)
703 int cols
= gnm_sheet_get_max_cols (sheet
);
704 int rows
= gnm_sheet_get_max_rows (sheet
);
706 debug_style_optimize
= gnm_debug_flag ("style-optimize");
708 sheet_style_sanity_check ();
710 sheet_style_init_size (sheet
, cols
, rows
);
714 sheet_style_resize (Sheet
*sheet
, int cols
, int rows
)
716 GnmStyleList
*styles
, *l
;
717 int old_cols
= gnm_sheet_get_max_cols (sheet
);
718 int old_rows
= gnm_sheet_get_max_rows (sheet
);
719 GnmRange save_range
, new_full
;
721 /* Save the style for the surviving area. */
722 range_init (&save_range
, 0, 0,
723 MIN (cols
, old_cols
) - 1, MIN (rows
, old_rows
) - 1);
724 styles
= sheet_style_get_range (sheet
, &save_range
);
726 /* Build new empty structures. */
727 sheet_style_shutdown (sheet
);
728 sheet_style_init_size (sheet
, cols
, rows
);
730 /* Reapply styles. */
731 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
732 for (l
= styles
; l
; l
= l
->next
) {
733 GnmStyleRegion
const *sr
= l
->data
;
734 GnmRange
const *r
= &sr
->range
;
735 GnmStyle
*style
= sr
->style
;
737 if (range_intersection (&newr
, r
, &new_full
))
738 sheet_style_apply_range2 (sheet
, &newr
, style
);
741 style_list_free (styles
);
746 cb_tile_pool_leak (gpointer data
, gpointer user
)
748 CellTile
*tile
= data
;
749 g_printerr ("Leaking tile at %p.\n", (void *)tile
);
754 sheet_style_shutdown (Sheet
*sheet
)
759 g_return_if_fail (IS_SHEET (sheet
));
760 g_return_if_fail (sheet
->style_data
!= NULL
);
763 * Clear all styles. This is an easy way to clear out all
764 * style dependencies.
766 range_init_full_sheet (&r
, sheet
);
767 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
769 cell_tile_dtor (sheet
->style_data
->styles
);
770 sheet
->style_data
->styles
= NULL
;
772 sheet
->style_data
->default_style
= NULL
;
774 /* Clear the pointer to the hash BEFORE clearing and add a test in
775 * sheet_style_unlink. If we don't then it is possible/probable that
776 * unlinking the styles will attempt to remove them from the hash while
779 table
= sheet
->style_data
->style_hash
;
780 sheet
->style_data
->style_hash
= NULL
;
781 g_slist_free_full (sh_all_styles (table
),
782 (GDestroyNotify
)gnm_style_unlink
);
784 style_color_unref (sheet
->style_data
->auto_pattern_color
);
786 g_free (sheet
->style_data
);
787 sheet
->style_data
= NULL
;
789 if (--active_sheet_count
== 0) {
791 go_mem_chunk_foreach_leak (tile_pools
[TILE_SIMPLE
],
792 cb_tile_pool_leak
, NULL
);
793 go_mem_chunk_destroy (tile_pools
[TILE_SIMPLE
], FALSE
);
794 tile_pools
[TILE_SIMPLE
] = NULL
;
796 go_mem_chunk_foreach_leak (tile_pools
[TILE_COL
],
797 cb_tile_pool_leak
, NULL
);
798 go_mem_chunk_destroy (tile_pools
[TILE_COL
], FALSE
);
799 tile_pools
[TILE_COL
] = NULL
;
801 go_mem_chunk_foreach_leak (tile_pools
[TILE_ROW
],
802 cb_tile_pool_leak
, NULL
);
803 go_mem_chunk_destroy (tile_pools
[TILE_ROW
], FALSE
);
804 tile_pools
[TILE_ROW
] = NULL
;
806 go_mem_chunk_foreach_leak (tile_pools
[TILE_MATRIX
],
807 cb_tile_pool_leak
, NULL
);
808 go_mem_chunk_destroy (tile_pools
[TILE_MATRIX
], FALSE
);
809 tile_pools
[TILE_MATRIX
] = NULL
;
811 /* If this fails one day, just make two pools. */
812 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
813 tile_pools
[TILE_PTR_MATRIX
] = NULL
;
815 if (tile_allocations
)
816 g_printerr ("Leaking %d style tiles.\n", tile_allocations
);
822 * sheet_style_set_auto_pattern_color:
824 * @grid_color: (transfer full): The color
826 * Set the color for rendering auto colored patterns in this sheet.
827 * Absorbs a reference to @pattern_color;
830 sheet_style_set_auto_pattern_color (Sheet
*sheet
, GnmColor
*pattern_color
)
832 g_return_if_fail (IS_SHEET (sheet
));
833 g_return_if_fail (sheet
->style_data
!= NULL
);
835 style_color_unref (sheet
->style_data
->auto_pattern_color
);
836 sheet
->style_data
->auto_pattern_color
= gnm_color_new_auto (pattern_color
->go_color
);
837 style_color_unref (pattern_color
);
841 * sheet_style_get_auto_pattern_color:
844 * Returns: (transfer full): the color for rendering auto colored patterns
848 sheet_style_get_auto_pattern_color (Sheet
const *sheet
)
851 g_return_val_if_fail (IS_SHEET (sheet
), style_color_black ());
852 g_return_val_if_fail (sheet
->style_data
!= NULL
, style_color_black ());
853 g_return_val_if_fail (sheet
->style_data
->auto_pattern_color
!= NULL
,
854 style_color_black ());
856 sc
= sheet
->style_data
->auto_pattern_color
;
857 style_color_ref (sc
);
863 * sheet_style_update_grid_color:
865 * This function updates the color of gnm_style_border_none when the sheet to be
866 * rendered is known. gnm_style_border_none tells how to render the
867 * grid. Because the grid color may be different for different sheets, the
868 * functions which render the grid call this function first. The rule for
869 * selecting the grid color, which is the same as in Excel, is: - if the
870 * auto pattern color is default (which is black), the grid color is gray,
871 * as returned by style_color_grid (). - otherwise, the auto pattern color
872 * is used for the grid.
875 sheet_style_update_grid_color (Sheet
const *sheet
)
877 GnmColor
*default_auto
= style_color_auto_pattern ();
878 GnmColor
*sheet_auto
= sheet_style_get_auto_pattern_color (sheet
);
879 GnmColor
*grid_color
= style_color_grid ();
882 new_color
= (style_color_equal (default_auto
, sheet_auto
)
883 ? grid_color
: sheet_auto
);
885 /* Do nothing if we already have the right color */
886 if (gnm_style_border_none()->color
!= new_color
) {
887 style_color_ref (new_color
); /* none_set eats the ref */
888 gnm_style_border_none_set_color (new_color
);
890 style_color_unref (grid_color
);
891 style_color_unref (sheet_auto
);
892 style_color_unref (default_auto
);
895 /****************************************************************************/
898 tile_is_uniform (CellTile
const *tile
)
900 const int s
= tile_size
[tile
->type
];
901 GnmStyle
const *st
= tile
->style_any
.style
[0];
904 for (i
= 1; i
< s
; i
++)
905 if (tile
->style_any
.style
[i
] != st
)
912 vector_apply_pstyle (CellTile
*tile
, ReplacementStyle
*rs
,
913 int cc
, int cr
, int level
, GnmRange
const *indic
)
915 const CellTileType type
= tile
->type
;
916 const int ncols
= tile_col_count
[type
];
917 const int nrows
= tile_row_count
[type
];
918 const int w1
= tile_widths
[level
+ 1] / ncols
;
919 const int h1
= tile_heights
[level
+ 1] / nrows
;
920 const int fcol
= indic
->start
.col
;
921 const int frow
= indic
->start
.row
;
922 const int lcol
= MIN (ncols
- 1, indic
->end
.col
);
923 const int lrow
= MIN (nrows
- 1, indic
->end
.row
);
924 GnmSheetSize
const *ss
= gnm_sheet_get_size (rs
->sheet
);
928 for (r
= frow
; r
<= lrow
; r
++) {
929 GnmStyle
**st
= tile
->style_any
.style
+ ncols
* r
;
930 rng
.start
.row
= cr
+ h1
* r
;
931 rng
.end
.row
= MIN (rng
.start
.row
+ (h1
- 1),
933 for (c
= fcol
; c
<= lcol
; c
++) {
934 rng
.start
.col
= cc
+ w1
* c
;
935 rng
.end
.col
= MIN (rng
.start
.col
+ (w1
- 1),
937 rstyle_apply (st
+ c
, rs
, &rng
);
943 * Determine whether before applying a style in the area of apply_to
944 * one needs to split the tile column-wise.
946 * If FALSE is returned then the tile need to be split to a TILE_PTR_MATRIX
947 * because the current level is not fine-grained enough.
949 * If TRUE is returned, TILE_SIMPLE needs to be split into TILE_COL and
950 * TILE_ROW needs to be split into TILE_MATRIX. TILE_COL and TILE_MATRIX
951 * should be kept. In indic, the inclusive post-split indicies of the
952 * range will be returned.
954 * If apply_to covers the entire tile, TRUE will be returned and the judgement
955 * on splitting above should be ignored. The indices in indic will be as-if
956 * the split was done.
959 col_indicies (int corner_col
, int w
, GnmRange
const *apply_to
,
964 i
= apply_to
->start
.col
- corner_col
;
966 indec
->start
.col
= 0;
971 indec
->start
.col
= tmp
;
974 i
= 1 + apply_to
->end
.col
- corner_col
;
976 if (tmp
>= TILE_SIZE_COL
)
977 indec
->end
.col
= TILE_SIZE_COL
- 1;
981 indec
->end
.col
= tmp
- 1;
987 /* See docs for col_indicies. Swap cols and rows. */
989 row_indicies (int corner_row
, int h
, GnmRange
const *apply_to
,
994 i
= apply_to
->start
.row
- corner_row
;
996 indic
->start
.row
= 0;
1001 indic
->start
.row
= tmp
;
1004 i
= 1 + apply_to
->end
.row
- corner_row
;
1006 if (tmp
>= TILE_SIZE_ROW
)
1007 indic
->end
.row
= TILE_SIZE_ROW
- 1;
1011 indic
->end
.row
= tmp
- 1;
1018 * cell_tile_apply: This is the primary logic for making changing areas in the
1019 * tree. It could be further optimised if it becomes a bottle neck.
1022 cell_tile_apply (CellTile
**tile
, int level
,
1023 int corner_col
, int corner_row
,
1024 GnmRange
const *apply_to
,
1025 ReplacementStyle
*rs
)
1027 int const width
= tile_widths
[level
+1];
1028 int const height
= tile_heights
[level
+1];
1029 int const w
= tile_widths
[level
];
1030 int const h
= tile_heights
[level
];
1031 gboolean
const full_width
= (apply_to
->start
.col
<= corner_col
&&
1032 apply_to
->end
.col
>= (corner_col
+width
-1));
1033 gboolean
const full_height
= (apply_to
->start
.row
<= corner_row
&&
1034 apply_to
->end
.row
>= (corner_row
+height
-1));
1039 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1040 g_return_if_fail (tile
!= NULL
);
1041 g_return_if_fail (*tile
!= NULL
);
1043 type
= (*tile
)->type
;
1044 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1046 /* applying the same style to part of a simple-tile is a nop */
1047 if (type
== TILE_SIMPLE
&&
1048 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1052 * Indices for the whole tile assuming a split to matrix.
1053 * We can still use these indices if we don't split either way.
1055 indic
.start
.col
= 0;
1056 indic
.start
.row
= 0;
1057 indic
.end
.col
= TILE_SIZE_COL
- 1;
1058 indic
.end
.row
= TILE_SIZE_ROW
- 1;
1060 if (type
== TILE_PTR_MATRIX
)
1062 else if (full_width
&& full_height
)
1064 else if (full_height
) {
1065 if (!col_indicies (corner_col
, w
, apply_to
, &indic
))
1066 goto split_to_ptr_matrix
;
1072 res
= cell_tile_style_new (
1073 (*tile
)->style_simple
.style
[0],
1075 cell_tile_dtor (*tile
);
1083 goto split_to_matrix
;
1085 g_assert_not_reached ();
1087 } else if (full_width
) {
1088 if (!row_indicies (corner_row
, h
, apply_to
, &indic
))
1089 goto split_to_ptr_matrix
;
1095 res
= cell_tile_style_new (
1096 (*tile
)->style_simple
.style
[0],
1098 cell_tile_dtor (*tile
);
1106 goto split_to_matrix
;
1108 g_assert_not_reached ();
1111 if (col_indicies (corner_col
, w
, apply_to
, &indic
) &&
1112 row_indicies (corner_row
, h
, apply_to
, &indic
))
1113 goto split_to_matrix
;
1115 goto split_to_ptr_matrix
;
1118 g_assert_not_reached ();
1121 *tile
= cell_tile_matrix_set (*tile
);
1124 vector_apply_pstyle (*tile
, rs
, corner_col
, corner_row
, level
, &indic
);
1128 CellTileOptimize cto
;
1129 cto
.ss
= gnm_sheet_get_size (rs
->sheet
);
1130 cto
.recursion
= FALSE
;
1131 cell_tile_optimize (tile
, level
, &cto
, corner_col
, corner_row
);
1135 split_to_ptr_matrix
:
1137 * We get here when apply_to's corners are not on a TILE_MATRIX grid.
1138 * Split to pointer matrix whose element tiles will have a finer grid.
1140 g_return_if_fail (type
!= TILE_PTR_MATRIX
);
1142 CellTile
*res
= cell_tile_ptr_matrix_new (*tile
);
1143 cell_tile_dtor (*tile
);
1145 type
= TILE_PTR_MATRIX
;
1149 g_return_if_fail (type
== TILE_PTR_MATRIX
);
1150 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1151 int const cr
= corner_row
+ h
*r
;
1152 if (cr
> apply_to
->end
.row
)
1154 if ((cr
+ h
) <= apply_to
->start
.row
)
1157 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1158 int const cc
= corner_col
+ w
*c
;
1159 if (cc
> apply_to
->end
.col
)
1161 if ((cc
+ w
) <= apply_to
->start
.col
)
1164 cell_tile_apply ((*tile
)->ptr_matrix
.ptr
+ i
+ c
,
1165 level
- 1, cc
, cr
, apply_to
, rs
);
1171 /* Handler for foreach_tile.
1173 * "width" and "height" refer to tile size which may extend beyond
1174 * the range supplied to foreach_tile and even beyond the sheet.
1176 typedef void (*ForeachTileFunc
) (GnmStyle
*style
,
1177 int corner_col
, int corner_row
,
1178 int width
, int height
,
1179 GnmRange
const *apply_to
, gpointer user
);
1181 foreach_tile_r (CellTile
*tile
, int level
,
1182 int corner_col
, int corner_row
,
1183 GnmRange
const *apply_to
,
1184 ForeachTileFunc handler
,
1187 int const width
= tile_widths
[level
+1];
1188 int const height
= tile_heights
[level
+1];
1189 int const w
= tile_widths
[level
];
1190 int const h
= tile_heights
[level
];
1193 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1194 g_return_if_fail (tile
!= NULL
);
1196 switch (tile
->type
) {
1198 handler (tile
->style_simple
.style
[0],
1199 corner_col
, corner_row
, width
, height
,
1204 if (apply_to
!= NULL
) {
1205 c
= (apply_to
->start
.col
- corner_col
) / w
;
1208 last
= (apply_to
->end
.col
- corner_col
) / w
+ 1;
1209 if (last
> TILE_SIZE_COL
)
1210 last
= TILE_SIZE_COL
;
1213 last
= TILE_SIZE_COL
;
1215 for (; c
< last
; ++c
)
1216 handler (tile
->style_col
.style
[c
],
1217 corner_col
+ c
*w
, corner_row
, w
, height
,
1222 if (apply_to
!= NULL
) {
1223 r
= (apply_to
->start
.row
- corner_row
) / h
;
1226 last
= (apply_to
->end
.row
- corner_row
) / h
+ 1;
1227 if (last
> TILE_SIZE_ROW
)
1228 last
= TILE_SIZE_ROW
;
1231 last
= TILE_SIZE_ROW
;
1233 for (; r
< last
; ++r
)
1234 handler (tile
->style_row
.style
[r
],
1235 corner_col
, corner_row
+ r
*h
, width
, h
,
1240 case TILE_PTR_MATRIX
:
1241 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1242 int const cr
= corner_row
+ h
*r
;
1244 if (cr
> apply_to
->end
.row
)
1246 if ((cr
+ h
) <= apply_to
->start
.row
)
1250 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1251 int const cc
= corner_col
+ w
*c
;
1253 if (cc
> apply_to
->end
.col
)
1255 if ((cc
+ w
) <= apply_to
->start
.col
)
1259 if (tile
->type
== TILE_MATRIX
) {
1260 handler (tile
->style_matrix
.style
[r
*TILE_SIZE_COL
+c
],
1263 w
, h
, apply_to
, user
);
1266 tile
->ptr_matrix
.ptr
[c
+ r
*TILE_SIZE_COL
],
1267 level
-1, cc
, cr
, apply_to
, handler
, user
);
1274 g_warning ("Adaptive Quad Tree corruption !");
1279 foreach_tile (Sheet
const *sheet
, GnmRange
const *apply_to
,
1280 ForeachTileFunc handler
, gpointer user
)
1282 foreach_tile_r (sheet
->style_data
->styles
,
1283 sheet
->tile_top_level
, 0, 0,
1284 apply_to
, handler
, user
);
1288 * cell_tile_apply_pos: This is an simplified version of cell_tile_apply. It
1289 * does not need all the bells and whistles because it operates on single cells.
1292 cell_tile_apply_pos (CellTile
**tile
, int level
,
1294 ReplacementStyle
*rs
)
1300 g_return_if_fail (col
>= 0);
1301 g_return_if_fail (col
< gnm_sheet_get_max_cols (rs
->sheet
));
1302 g_return_if_fail (row
>= 0);
1303 g_return_if_fail (row
< gnm_sheet_get_max_rows (rs
->sheet
));
1305 range_init (&rng
, col
, row
, col
, row
);
1308 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1309 g_return_if_fail (tile
!= NULL
);
1310 g_return_if_fail (*tile
!= NULL
);
1314 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1317 int const w
= tile_widths
[level
];
1318 int const c
= col
/ w
;
1319 int const h
= tile_heights
[level
];
1320 int const r
= row
/ h
;
1322 if (type
!= TILE_PTR_MATRIX
) {
1323 /* applying the same style to part of a simple-tile is a nop */
1324 if (type
== TILE_SIMPLE
&&
1325 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1328 tmp
= cell_tile_ptr_matrix_new (tmp
);
1329 cell_tile_dtor (*tile
);
1332 tile
= tmp
->ptr_matrix
.ptr
+ r
* TILE_SIZE_COL
+ c
;
1336 goto tail_recursion
;
1337 } else if (type
!= TILE_MATRIX
)
1338 *tile
= tmp
= cell_tile_matrix_set (tmp
);
1340 g_return_if_fail (tmp
->type
== TILE_MATRIX
);
1341 rstyle_apply (tmp
->style_matrix
.style
+ row
* TILE_SIZE_COL
+ col
,
1347 * sheet_style_set_range:
1352 * Change the complete style for a region.
1353 * This function absorbs a reference to the new @style.
1356 sheet_style_set_range (Sheet
*sheet
, GnmRange
const *range
,
1359 ReplacementStyle rs
;
1362 g_return_if_fail (IS_SHEET (sheet
));
1363 g_return_if_fail (range
!= NULL
);
1365 if (range
->start
.col
> range
->end
.col
||
1366 range
->start
.row
> range
->end
.row
) {
1367 gnm_style_unref (style
);
1372 range_ensure_sanity (&r
, sheet
);
1374 rstyle_ctor_style (&rs
, style
, sheet
);
1375 cell_tile_apply (&sheet
->style_data
->styles
,
1376 sheet
->tile_top_level
, 0, 0,
1382 * sheet_style_apply_col:
1387 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1388 * will make life easier.
1390 * Apply a partial style to a full col.
1391 * The routine absorbs a reference to the partial style.
1394 sheet_style_apply_col (Sheet
*sheet
, int col
, GnmStyle
*pstyle
)
1397 range_init_cols (&r
, sheet
, col
, col
);
1398 sheet_style_apply_range (sheet
, &r
, pstyle
);
1402 * sheet_style_apply_row:
1407 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1408 * will make life easier.
1410 * Apply a partial style to a full col.
1411 * The routine absorbs a reference to the partial style.
1414 sheet_style_apply_row (Sheet
*sheet
, int row
, GnmStyle
*pstyle
)
1417 range_init_rows (&r
, sheet
, row
, row
);
1418 sheet_style_apply_range (sheet
, &r
, pstyle
);
1422 * sheet_style_apply_pos:
1428 * Apply a partial style to a single cell
1429 * This function absorbs a reference to the new @style.
1432 sheet_style_apply_pos (Sheet
*sheet
, int col
, int row
,
1435 ReplacementStyle rs
;
1437 g_return_if_fail (IS_SHEET (sheet
));
1439 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1440 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1441 sheet
->tile_top_level
, col
, row
,
1446 * sheet_style_set_pos:
1452 * Change the complete style for a single cell.
1453 * This function absorbs a reference to the new @style.
1456 sheet_style_set_pos (Sheet
*sheet
, int col
, int row
,
1459 ReplacementStyle rs
;
1461 g_return_if_fail (IS_SHEET (sheet
));
1463 rstyle_ctor_style (&rs
, style
, sheet
);
1464 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1465 sheet
->tile_top_level
, col
, row
,
1471 * sheet_style_default:
1474 * Returns a reference to default style for a sheet.
1477 sheet_style_default (Sheet
const *sheet
)
1479 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1480 g_return_val_if_fail (sheet
->style_data
!= NULL
, NULL
);
1482 gnm_style_ref (sheet
->style_data
->default_style
);
1483 return sheet
->style_data
->default_style
;
1489 * @col: column number
1492 * Returns: (transfer none): find the fully qualified style applicable to
1493 * the specified cell position
1496 sheet_style_get (Sheet
const *sheet
, int col
, int row
)
1498 int level
= sheet
->tile_top_level
;
1499 CellTile
*tile
= sheet
->style_data
->styles
;
1502 int width
= tile_widths
[level
];
1503 int height
= tile_heights
[level
];
1504 int c
= col
/ width
;
1505 int r
= row
/ height
;
1507 g_return_val_if_fail (tile
!= NULL
, NULL
);
1508 g_return_val_if_fail (0 <= c
&& c
< TILE_SIZE_COL
, NULL
);
1509 g_return_val_if_fail (0 <= r
&& r
< TILE_SIZE_ROW
, NULL
);
1511 switch (tile
->type
) {
1513 return tile
->style_simple
.style
[0];
1515 return tile
->style_col
.style
[c
];
1517 return tile
->style_row
.style
[r
];
1519 return tile
->style_matrix
.style
[r
* TILE_SIZE_COL
+ c
];
1521 case TILE_PTR_MATRIX
:
1522 g_return_val_if_fail (level
> 0, NULL
);
1525 tile
= tile
->ptr_matrix
.ptr
[r
* TILE_SIZE_COL
+ c
];
1531 g_warning ("Adaptive Quad Tree corruption !");
1537 #define border_null(b) ((b) == none || (b) == NULL)
1540 style_row (GnmStyle
const *style
, int start_col
, int end_col
,
1541 GnmStyleRow
*sr
, gboolean accept_conditions
)
1543 GnmBorder
const *top
, *bottom
, *none
= gnm_style_border_none ();
1544 GnmBorder
const *left
, *right
, *v
;
1545 int const end
= MIN (end_col
, sr
->end_col
);
1546 int i
= MAX (start_col
, sr
->start_col
);
1547 GnmStyleConditions
*conds
;
1549 conds
= accept_conditions
1550 ? gnm_style_get_conditions (style
)
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 (conds
, &ep
);
1559 ? gnm_style_get_cond_style (style
, res
)
1561 ep
.eval
.col
, ep
.eval
.col
, sr
, FALSE
);
1566 top
= gnm_style_get_border (style
, MSTYLE_BORDER_TOP
);
1567 bottom
= gnm_style_get_border (style
, MSTYLE_BORDER_BOTTOM
);
1568 left
= gnm_style_get_border (style
, MSTYLE_BORDER_LEFT
);
1569 right
= gnm_style_get_border (style
, MSTYLE_BORDER_RIGHT
);
1571 /* Cancel grids if there is a background */
1572 if (sr
->hide_grid
|| gnm_style_get_pattern (style
) > 0) {
1583 if (left
!= none
&& border_null (sr
->vertical
[i
]))
1584 sr
->vertical
[i
] = left
;
1585 v
= border_null (right
) ? left
: right
;
1588 sr
->styles
[i
] = style
;
1589 if (top
!= none
&& border_null (sr
->top
[i
]))
1591 sr
->bottom
[i
] = bottom
;
1592 sr
->vertical
[++i
] = v
;
1594 if (border_null (right
))
1595 sr
->vertical
[i
] = right
;
1599 get_style_row (CellTile
const *tile
, int level
,
1600 int corner_col
, int corner_row
,
1603 int const width
= tile_widths
[level
+1];
1604 int const w
= tile_widths
[level
];
1605 int const h
= tile_heights
[level
];
1609 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1610 g_return_if_fail (tile
!= NULL
);
1614 if (t
!= TILE_SIMPLE
&& t
!= TILE_COL
) {
1615 r
= (sr
->row
> corner_row
) ? (sr
->row
- corner_row
)/ h
: 0;
1616 g_return_if_fail (r
< TILE_SIZE_ROW
);
1619 if (t
== TILE_ROW
|| t
== TILE_SIMPLE
) {
1620 style_row (tile
->style_any
.style
[r
],
1621 corner_col
, corner_col
+ width
- 1, sr
, TRUE
);
1623 /* find the start and end */
1625 int last_c
= (sr
->end_col
- corner_col
) / w
;
1626 if (last_c
>= TILE_SIZE_COL
)
1627 last_c
= TILE_SIZE_COL
-1;
1628 if (sr
->start_col
> corner_col
) {
1629 c
= (sr
->start_col
- corner_col
) / w
;
1630 corner_col
+= c
* w
;
1636 if (t
!= TILE_PTR_MATRIX
) {
1637 GnmStyle
* const *styles
= tile
->style_any
.style
+ r
*TILE_SIZE_COL
;
1639 for ( ; c
<= last_c
; c
++, corner_col
+= w
)
1640 style_row (styles
[c
],
1641 corner_col
, corner_col
+ w
- 1, sr
, TRUE
);
1643 CellTile
* const *tiles
= tile
->ptr_matrix
.ptr
+ r
*TILE_SIZE_COL
;
1645 g_return_if_fail (level
> 0);
1647 for ( level
-- ; c
<= last_c
; c
++, corner_col
+= w
)
1648 get_style_row (tiles
[c
], level
,
1649 corner_col
, corner_row
, sr
);
1655 * sheet_style_get_row:
1659 * A utility routine which efficiently retrieves a range of styles within a row.
1660 * It also merges adjacent borders as necessary.
1663 sheet_style_get_row (Sheet
const *sheet
, GnmStyleRow
*sr
)
1666 g_return_if_fail (IS_SHEET (sheet
));
1667 g_return_if_fail (sr
!= NULL
);
1668 g_return_if_fail (sr
->styles
!= NULL
);
1669 g_return_if_fail (sr
->vertical
!= NULL
);
1670 g_return_if_fail (sr
->top
!= NULL
);
1671 g_return_if_fail (sr
->bottom
!= NULL
);
1674 sr
->vertical
[sr
->start_col
] = gnm_style_border_none ();
1675 get_style_row (sheet
->style_data
->styles
, sheet
->tile_top_level
, 0, 0, sr
);
1679 cb_get_row (GnmStyle
*style
,
1680 int corner_col
, G_GNUC_UNUSED
int corner_row
,
1681 int width
, G_GNUC_UNUSED
int height
,
1682 GnmRange
const *apply_to
, gpointer user_
)
1684 GnmStyle
**res
= user_
;
1687 /* The given dimensions refer to the tile, not the area. */
1688 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
1690 for (i
= 0; i
< width
; i
++)
1691 res
[corner_col
+ i
] = style
;
1695 sheet_style_get_row2 (Sheet
const *sheet
, int row
)
1698 GnmStyle
**res
= g_new (GnmStyle
*, gnm_sheet_get_max_cols (sheet
));
1700 range_init_rows (&r
, sheet
, row
, row
);
1702 foreach_tile (sheet
, &r
, cb_get_row
, res
);
1711 * A small utility routine to initialize the grid drawing GnmStyleRow data
1715 style_row_init (GnmBorder
const * * *prev_vert
,
1716 GnmStyleRow
*sr
, GnmStyleRow
*next_sr
,
1717 int start_col
, int end_col
, gpointer mem
, gboolean hide_grid
)
1720 GnmBorder
const *none
= hide_grid
? NULL
: gnm_style_border_none ();
1722 /* alias the arrays for easy access so that array[col] is valid
1723 * for all elements start_col-1 .. end_col+1 inclusive.
1724 * Note that this means that in some cases array[-1] is legal.
1726 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
1728 sr
->vertical
-= start_col
-1;
1729 sr
->top
= sr
->vertical
+ n
;
1730 sr
->bottom
= sr
->top
+ n
;
1731 next_sr
->top
= sr
->bottom
; /* yes they should share */
1732 next_sr
->bottom
= next_sr
->top
+ n
;
1733 next_sr
->vertical
= next_sr
->bottom
+ n
;
1734 *prev_vert
= next_sr
->vertical
+ n
;
1735 sr
->styles
= ((GnmStyle
const **) (*prev_vert
+ n
));
1736 next_sr
->styles
= sr
->styles
+ n
;
1737 sr
->start_col
= next_sr
->start_col
= start_col
;
1738 sr
->end_col
= next_sr
->end_col
= end_col
;
1739 sr
->hide_grid
= next_sr
->hide_grid
= hide_grid
;
1741 /* Init the areas that sheet_style_get_row will not */
1742 for (col
= start_col
-1 ; col
<= end_col
+1; ++col
)
1743 (*prev_vert
)[col
] = sr
->top
[col
] = none
;
1744 sr
->vertical
[start_col
-1] = sr
->vertical
[end_col
+1] =
1745 next_sr
->vertical
[start_col
-1] = next_sr
->vertical
[end_col
+1] =
1746 next_sr
->top
[start_col
-1] = next_sr
->top
[end_col
+1] =
1747 next_sr
->bottom
[start_col
-1] = next_sr
->bottom
[end_col
+1] = none
;
1751 * sheet_style_apply_range: (skip)
1753 * @range: #GnmRange to apply over
1754 * @pstyle: (transfer full): A partial style to apply
1756 * Apply a partial style to a region.
1757 * The routine absorbs a reference to the partial style.
1760 sheet_style_apply_range (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1762 ReplacementStyle rs
;
1765 g_return_if_fail (IS_SHEET (sheet
));
1766 g_return_if_fail (range
!= NULL
);
1768 if (range
->start
.col
> range
->end
.col
||
1769 range
->start
.row
> range
->end
.row
) {
1770 gnm_style_unref (pstyle
);
1775 range_ensure_sanity (&r
, sheet
);
1777 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1778 cell_tile_apply (&sheet
->style_data
->styles
,
1779 sheet
->tile_top_level
, 0, 0,
1785 * sheet_style_apply_range2: (skip)
1787 * @range: #GnmRange to apply over
1788 * @pstyle: A partial style to apply
1790 * Apply a partial style to a region.
1793 sheet_style_apply_range2 (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1795 gnm_style_ref (pstyle
);
1796 sheet_style_apply_range (sheet
, range
, pstyle
);
1801 apply_border (Sheet
*sheet
, GnmRange
const *r
,
1802 GnmStyleBorderLocation side
,
1805 GnmStyle
*pstyle
= gnm_style_new ();
1806 pstyle_set_border (pstyle
, border
, side
);
1807 sheet_style_apply_range (sheet
, r
, pstyle
);
1811 * sheet_style_apply_border:
1816 * When a user applies a border to a region we attempt to remove the border
1817 * from the opposing side to avoid overlapping border specifications.
1819 * if we apply a top border to a range, we would clear the bottom border
1820 * of the range offset upwards.
1823 sheet_style_apply_border (Sheet
*sheet
,
1824 GnmRange
const *range
,
1825 GnmBorder
**borders
)
1827 GnmStyle
*pstyle
= NULL
;
1829 if (borders
== NULL
)
1832 if (borders
[GNM_STYLE_BORDER_TOP
]) {
1834 GnmRange r
= *range
;
1835 r
.end
.row
= r
.start
.row
;
1836 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1837 borders
[GNM_STYLE_BORDER_TOP
]);
1841 if (r
.start
.row
>= 0) {
1842 r
.end
.row
= r
.start
.row
;
1843 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1844 gnm_style_border_none ());
1848 if (borders
[GNM_STYLE_BORDER_BOTTOM
]) {
1849 /* 2.1 bottom inner */
1850 GnmRange r
= *range
;
1851 r
.start
.row
= r
.end
.row
;
1852 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1853 borders
[GNM_STYLE_BORDER_BOTTOM
]);
1855 /* 2.2 bottom outer */
1857 if (r
.end
.row
< gnm_sheet_get_last_row (sheet
)) {
1858 r
.start
.row
= r
.end
.row
;
1859 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1860 gnm_style_border_none ());
1864 if (borders
[GNM_STYLE_BORDER_LEFT
]) {
1865 /* 3.1 left inner */
1866 GnmRange r
= *range
;
1867 r
.end
.col
= r
.start
.col
;
1868 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1869 borders
[GNM_STYLE_BORDER_LEFT
]);
1871 /* 3.2 left outer */
1873 if (r
.start
.col
>= 0) {
1874 r
.end
.col
= r
.start
.col
;
1875 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1876 gnm_style_border_none ());
1880 if (borders
[GNM_STYLE_BORDER_RIGHT
]) {
1881 /* 4.1 right inner */
1882 GnmRange r
= *range
;
1883 r
.start
.col
= r
.end
.col
;
1884 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1885 borders
[GNM_STYLE_BORDER_RIGHT
]);
1887 /* 4.2 right outer */
1889 if (r
.end
.col
< gnm_sheet_get_last_col (sheet
)) {
1890 r
.start
.col
= r
.end
.col
;
1891 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1892 gnm_style_border_none ());
1896 /* Interiors horizontal : prefer top */
1897 if (borders
[GNM_STYLE_BORDER_HORIZ
] != NULL
) {
1898 /* 5.1 horizontal interior top */
1899 if (range
->start
.row
!= range
->end
.row
) {
1900 GnmRange r
= *range
;
1902 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1903 borders
[GNM_STYLE_BORDER_HORIZ
]);
1905 /* 5.2 interior bottom */
1906 if (range
->start
.row
!= range
->end
.row
) {
1907 GnmRange r
= *range
;
1909 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1910 gnm_style_border_none ());
1914 /* Interiors vertical: prefer left */
1915 if (borders
[GNM_STYLE_BORDER_VERT
] != NULL
) {
1916 /* 6.1 vertical interior left */
1917 if (range
->start
.col
!= range
->end
.col
) {
1918 GnmRange r
= *range
;
1920 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1921 borders
[GNM_STYLE_BORDER_VERT
]);
1924 /* 6.2 The vertical interior right */
1925 if (range
->start
.col
!= range
->end
.col
) {
1926 GnmRange r
= *range
;
1928 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1929 gnm_style_border_none ());
1933 /* 7. Diagonals (apply both in one pass) */
1934 if (borders
[GNM_STYLE_BORDER_DIAG
] != NULL
) {
1935 pstyle
= gnm_style_new ();
1936 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_DIAG
],
1937 GNM_STYLE_BORDER_DIAG
);
1939 if (borders
[GNM_STYLE_BORDER_REV_DIAG
]) {
1941 pstyle
= gnm_style_new ();
1942 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_REV_DIAG
],
1943 GNM_STYLE_BORDER_REV_DIAG
);
1946 sheet_style_apply_range (sheet
, range
, pstyle
);
1949 /****************************************************************************/
1953 unsigned int conflicts
;
1957 cb_find_conflicts (GnmStyle
*style
,
1958 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
1959 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
1960 G_GNUC_UNUSED GnmRange
const *apply_to
, FindConflicts
*ptr
)
1962 ptr
->conflicts
= gnm_style_find_conflicts (ptr
->accum
, style
, ptr
->conflicts
);
1966 border_mask_internal (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1967 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1968 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1972 gnm_style_border_unref (borders
[l
]);
1973 borders
[l
] = (GnmBorder
*)b
;
1974 gnm_style_border_ref (borders
[l
]);
1975 } else if (borders
[l
] != b
&& borders
[l
] != NULL
) {
1976 gnm_style_border_unref (borders
[l
]);
1982 border_mask (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1983 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1984 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1987 b
= gnm_style_border_none ();
1988 border_mask_internal (known
, borders
, b
, l
);
1992 border_mask_vec (gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
],
1993 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
],
1994 GnmBorder
const * const *vec
, int first
, int last
,
1995 GnmStyleBorderLocation l
)
1997 GnmBorder
const *b
= vec
[first
];
2000 b
= gnm_style_border_none ();
2001 while (first
++ < last
) {
2002 GnmBorder
const *tmp
= vec
[first
];
2004 tmp
= gnm_style_border_none ();
2011 border_mask_internal (known
, borders
, b
, l
);
2015 * sheet_style_find_conflicts:
2016 * @sheet: #Sheet to query
2017 * @r: #GnmRange to query
2019 * @borders: (out) (array fixed-size=8):
2021 * Returns: bitmask of conflicts
2024 sheet_style_find_conflicts (Sheet
const *sheet
, GnmRange
const *r
,
2026 GnmBorder
*borders
[GNM_STYLE_BORDER_EDGE_MAX
])
2028 int n
, col
, row
, start_col
, end_col
;
2030 gpointer
*sr_array_data
;
2031 GnmStyleBorderLocation i
;
2032 gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
];
2033 GnmBorder
const *none
= gnm_style_border_none ();
2036 g_return_val_if_fail (IS_SHEET (sheet
), 0);
2037 g_return_val_if_fail (r
!= NULL
, 0);
2038 g_return_val_if_fail (style
!= NULL
, 0);
2039 g_return_val_if_fail (borders
!= NULL
, 0);
2041 /* init style set with a copy of the top left corner of the 1st range */
2042 if (*style
== NULL
) {
2043 GnmStyle
const *tmp
= sheet_style_get (sheet
, r
->start
.col
, r
->start
.row
);
2044 *style
= gnm_style_dup (tmp
);
2045 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2047 borders
[i
] = gnm_style_border_ref ((GnmBorder
*)none
);
2050 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2056 user
.accum
= *style
;
2057 user
.conflicts
= 0; /* no conflicts yet */
2058 foreach_tile (sheet
, r
, (ForeachTileFunc
)cb_find_conflicts
, &user
);
2060 /* copy over the diagonals */
2061 for (i
= GNM_STYLE_BORDER_REV_DIAG
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2062 GnmStyleElement se
= GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (i
);
2063 gnm_style_border_unref (borders
[i
]);
2064 if (user
.conflicts
& (1 << se
))
2067 borders
[i
] = gnm_style_border_ref (
2068 gnm_style_get_border (*style
, se
));
2071 start_col
= r
->start
.col
;
2072 if (r
->start
.col
> 0)
2074 end_col
= r
->end
.col
;
2075 if (r
->end
.col
< gnm_sheet_get_max_cols (sheet
))
2078 /* allocate then alias the arrays for easy access */
2079 n
= end_col
- start_col
+ 2;
2080 g_assert (sizeof (GnmBorder
*) == sizeof (gpointer
));
2081 g_assert (sizeof (GnmStyle
*) == sizeof (gpointer
));
2082 sr_array_data
= g_new (gpointer
, n
* 4);
2083 sr
.vertical
= (GnmBorder
const **)(sr_array_data
- start_col
);
2084 sr
.top
= (GnmBorder
const **)(sr_array_data
+ n
- start_col
);
2085 sr
.bottom
= (GnmBorder
const **)(sr_array_data
+ 2 * n
- start_col
);
2086 sr
.styles
= (GnmStyle
const **) (sr_array_data
+ 3 * n
- start_col
);
2087 sr
.start_col
= start_col
;
2088 sr
.end_col
= end_col
;
2089 sr
.hide_grid
= sheet
->hide_grid
;
2091 /* pretend the previous bottom had no borders */
2092 for (col
= start_col
; col
<= end_col
; ++col
)
2095 /* merge the bottom of the previous row */
2096 if (r
->start
.row
> 0) {
2097 GnmBorder
const ** roller
;
2098 sr
.row
= r
->start
.row
- 1;
2099 sheet_style_get_row (sheet
, &sr
);
2100 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2104 * TODO: The border handling is tricky and currently VERY slow for
2105 * large ranges. We could easily optimize this. There is no need to
2106 * retrieve the style in every cell just to do a filter for uniformity
2107 * by row. One day we should do a special case version of
2108 * sheet_style_get_row probably style_get_uniform_col (this will be
2111 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
2112 GnmBorder
const **roller
;
2114 sheet_style_get_row (sheet
, &sr
);
2116 border_mask (known
, borders
, sr
.vertical
[r
->start
.col
],
2117 GNM_STYLE_BORDER_LEFT
);
2118 border_mask (known
, borders
, sr
.vertical
[r
->end
.col
+1],
2119 GNM_STYLE_BORDER_RIGHT
);
2120 border_mask_vec (known
, borders
, sr
.top
,
2121 r
->start
.col
, r
->end
.col
, (row
== r
->start
.row
)
2122 ? GNM_STYLE_BORDER_TOP
: GNM_STYLE_BORDER_HORIZ
);
2123 if (r
->start
.col
!= r
->end
.col
)
2124 border_mask_vec (known
, borders
, sr
.vertical
,
2125 r
->start
.col
+1, r
->end
.col
,
2126 GNM_STYLE_BORDER_VERT
);
2128 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2131 /* merge the top of the next row */
2132 if (r
->end
.row
< gnm_sheet_get_last_row (sheet
)) {
2133 sr
.row
= r
->end
.row
+ 1;
2134 sheet_style_get_row (sheet
, &sr
);
2136 border_mask_vec (known
, borders
, sr
.top
, r
->start
.col
, r
->end
.col
,
2137 GNM_STYLE_BORDER_BOTTOM
);
2139 g_free (sr_array_data
);
2140 return user
.conflicts
;
2144 * sheet_style_relocate:
2147 * Slide the styles from the origin region to the new position.
2150 sheet_style_relocate (GnmExprRelocateInfo
const *rinfo
)
2153 GnmStyleList
*styles
;
2155 g_return_if_fail (rinfo
!= NULL
);
2157 styles
= sheet_style_get_range (rinfo
->origin_sheet
, &rinfo
->origin
);
2159 sheet_style_set_range (rinfo
->origin_sheet
, &rinfo
->origin
,
2160 sheet_style_default (rinfo
->origin_sheet
));
2161 corner
.col
= rinfo
->origin
.start
.col
+ rinfo
->col_offset
;
2162 corner
.row
= rinfo
->origin
.start
.row
+ rinfo
->row_offset
;
2163 sheet_style_set_list (rinfo
->target_sheet
, &corner
, styles
, NULL
, NULL
);
2164 style_list_free (styles
);
2168 * sheet_style_insdel_colrow:
2171 * Insert of delete style columns/rows.
2173 * For the insert case, we stretch the preceding column/row into there space
2177 sheet_style_insdel_colrow (GnmExprRelocateInfo
const *rinfo
)
2179 GnmStyleList
*styles
= NULL
;
2184 g_return_if_fail (rinfo
!= NULL
);
2185 g_return_if_fail (rinfo
->origin_sheet
== rinfo
->target_sheet
);
2186 g_return_if_fail ((rinfo
->col_offset
== 0) != (rinfo
->row_offset
== 0));
2188 is_insert
= (rinfo
->col_offset
+ rinfo
->row_offset
> 0);
2189 sheet
= rinfo
->origin_sheet
;
2192 /* 1) copy col/row to the top/left of the region, and extend it */
2193 corner
= rinfo
->origin
.start
;
2194 if (rinfo
->col_offset
) {
2195 int col
= MAX (corner
.col
- 1, 0);
2200 range_init_cols (&r
, sheet
, col
, col
);
2201 styles
= sheet_style_get_range (sheet
, &r
);
2202 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2203 GnmStyleRegion
*sr
= ptr
->data
;
2204 sr
->range
.end
.col
= rinfo
->col_offset
- 1;
2207 int row
= MAX (corner
.row
- 1, 0);
2212 range_init_rows (&r
, sheet
, row
, row
);
2213 styles
= sheet_style_get_range (sheet
, &r
);
2214 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2215 GnmStyleRegion
*sr
= ptr
->data
;
2216 sr
->range
.end
.row
= rinfo
->row_offset
- 1;
2221 sheet_style_relocate (rinfo
);
2224 sheet_style_set_list (sheet
, &corner
, styles
, NULL
, NULL
);
2225 style_list_free (styles
);
2230 cb_style_extent (GnmStyle
*style
,
2231 int corner_col
, int corner_row
, int width
, int height
,
2232 GnmRange
const *apply_to
, gpointer user
)
2234 GnmRange
*res
= user
;
2235 if (gnm_style_visible_in_blank (style
)) {
2238 /* The given dimensions refer to the tile, not the area. */
2239 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2240 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2242 tmp
= corner_col
+width
-1;
2243 if (res
->end
.col
< tmp
)
2245 if (res
->start
.col
> corner_col
)
2246 res
->start
.col
= corner_col
;
2248 tmp
= corner_row
+height
-1;
2249 if (res
->end
.row
< tmp
)
2251 if (res
->start
.row
> corner_row
)
2252 res
->start
.row
= corner_row
;
2257 * sheet_style_get_extent:
2258 * @sheet: sheet to measure
2259 * @r: starting range and resulting range
2261 * A simple implementation that finds the smallest range containing all visible styles
2262 * and containing @res.
2265 sheet_style_get_extent (Sheet
const *sheet
, GnmRange
*res
)
2269 range_init_full_sheet (&r
, sheet
);
2270 foreach_tile (sheet
, &r
, cb_style_extent
, res
);
2273 struct cb_nondefault_extent
{
2275 GnmStyle
**col_defaults
;
2279 cb_nondefault_extent (GnmStyle
*style
,
2280 int corner_col
, int corner_row
, int width
, int height
,
2281 GnmRange
const *apply_to
, gpointer user_
)
2283 struct cb_nondefault_extent
*user
= user_
;
2284 GnmRange
*res
= user
->res
;
2287 for (i
= 0; i
< width
; i
++) {
2288 int col
= corner_col
+ i
;
2289 if (col
>= apply_to
->start
.col
&&
2290 col
<= apply_to
->end
.col
&&
2291 style
!= user
->col_defaults
[col
]) {
2292 int max_row
= MIN (corner_row
+ height
- 1,
2294 int min_row
= MAX (corner_row
, apply_to
->start
.row
);
2296 res
->start
.col
= MIN (col
, res
->start
.col
);
2297 res
->start
.row
= MIN (min_row
, res
->start
.row
);
2299 res
->end
.col
= MAX (col
, res
->end
.col
);
2300 res
->end
.row
= MAX (max_row
, res
->end
.row
);
2306 sheet_style_get_nondefault_extent (Sheet
const *sheet
, GnmRange
*extent
,
2307 const GnmRange
*src
, GnmStyle
**col_defaults
)
2309 struct cb_nondefault_extent user
;
2311 user
.col_defaults
= col_defaults
;
2312 foreach_tile (sheet
, src
, cb_nondefault_extent
, &user
);
2315 struct cb_is_default
{
2317 GnmStyle
**col_defaults
;
2321 cb_is_default (GnmStyle
*style
,
2322 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2323 int width
, G_GNUC_UNUSED
int height
,
2324 GnmRange
const *apply_to
, gpointer user_
)
2326 struct cb_is_default
*user
= user_
;
2329 /* The given "width" refers to the tile, not the area. */
2330 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2332 for (i
= 0; user
->res
&& i
< width
; i
++) {
2333 if (style
!= user
->col_defaults
[corner_col
+ i
])
2339 sheet_style_is_default (Sheet
const *sheet
, const GnmRange
*r
, GnmStyle
**col_defaults
)
2341 struct cb_is_default user
;
2344 user
.col_defaults
= col_defaults
;
2346 foreach_tile (sheet
, r
, cb_is_default
, &user
);
2351 struct cb_get_nondefault
{
2353 GnmStyle
**col_defaults
;
2357 cb_get_nondefault (GnmStyle
*style
,
2358 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2359 int width
, G_GNUC_UNUSED
int height
,
2360 GnmRange
const *apply_to
, gpointer user_
)
2362 struct cb_get_nondefault
*user
= user_
;
2365 /* The given dimensions refer to the tile, not the area. */
2366 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2367 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2369 for (i
= 0; i
< width
; i
++) {
2370 if (style
!= user
->col_defaults
[corner_col
+ i
]) {
2372 for (j
= 0; j
< height
; j
++)
2373 user
->res
[corner_row
+ j
] = 1;
2380 sheet_style_get_nondefault_rows (Sheet
const *sheet
, GnmStyle
**col_defaults
)
2382 struct cb_get_nondefault user
;
2385 range_init_full_sheet (&r
, sheet
);
2387 user
.res
= g_new0 (guint8
, gnm_sheet_get_max_rows (sheet
));
2388 user
.col_defaults
= col_defaults
;
2390 foreach_tile (sheet
, &r
, cb_get_nondefault
, &user
);
2395 struct cb_most_common
{
2402 cb_most_common (GnmStyle
*style
,
2403 int corner_col
, int corner_row
, int width
, int height
,
2404 GnmRange
const *apply_to
, gpointer user
)
2406 struct cb_most_common
*cmc
= user
;
2407 int *counts
= g_hash_table_lookup (cmc
->h
, style
);
2410 counts
= g_new0 (int, cmc
->l
);
2411 g_hash_table_insert (cmc
->h
, style
, counts
);
2414 /* The given dimensions refer to the tile, not the area. */
2415 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2416 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2419 for (i
= 0; i
< width
; i
++)
2420 counts
[corner_col
+ i
] += height
;
2422 for (i
= 0; i
< height
; i
++)
2423 counts
[corner_row
+ i
] += width
;
2427 * sheet_style_most_common:
2428 * @sheet: sheet to inspect
2429 * @is_col: if %TRUE, look for common styles in columns; if FALSE, look in rows.
2431 * Returns: an array of styles describing the most common styles, one per column
2435 sheet_style_most_common (Sheet
const *sheet
, gboolean is_col
)
2438 struct cb_most_common cmc
;
2441 GHashTableIter iter
;
2442 gpointer key
, value
;
2444 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2446 range_init_full_sheet (&r
, sheet
);
2447 cmc
.h
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, g_free
);
2448 cmc
.l
= colrow_max (is_col
, sheet
);
2449 cmc
.is_col
= is_col
;
2450 foreach_tile (sheet
, &r
, cb_most_common
, &cmc
);
2452 max
= g_new0 (int, cmc
.l
);
2453 res
= g_new0 (GnmStyle
*, cmc
.l
);
2454 g_hash_table_iter_init (&iter
, cmc
.h
);
2455 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
2456 int *counts
= value
;
2457 GnmStyle
*style
= key
;
2459 for (j
= 0; j
< cmc
.l
; j
++) {
2460 /* FIXME: we really ought to break ties in a
2461 consistent way that does not depend on hash
2463 if (counts
[j
] > max
[j
]) {
2469 g_hash_table_destroy (cmc
.h
);
2475 /****************************************************************************/
2478 * gnm_style_region_new:
2482 * Returns: (transfer full): the newly allocated #GnmStyleRegion.
2485 gnm_style_region_new (GnmRange
const *range
, GnmStyle
*style
)
2489 sr
= g_new (GnmStyleRegion
, 1);
2492 gnm_style_ref (style
);
2498 gnm_style_region_free (GnmStyleRegion
*sr
)
2500 g_return_if_fail (sr
!= NULL
);
2502 gnm_style_unref (sr
->style
);
2507 static GnmStyleRegion
*
2508 gnm_style_region_copy (GnmStyleRegion
*sr
)
2510 GnmStyleRegion
*res
= g_new (GnmStyleRegion
, 1);
2512 gnm_style_ref (sr
->style
);
2517 gnm_style_region_get_type (void)
2522 t
= g_boxed_type_register_static ("GnmStyleRegion",
2523 (GBoxedCopyFunc
)gnm_style_region_copy
,
2524 (GBoxedFreeFunc
)gnm_style_region_free
);
2531 debug_style_list (void)
2533 static int debug
= -1;
2535 debug
= gnm_debug_flag ("style-list");
2541 GHashTable
*by_tl
, *by_br
;
2543 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
);
2544 gboolean (*style_filter
) (GnmStyle
const *style
);
2545 GnmSheetSize
const *sheet_size
;
2549 merge_ranges (GnmRange
*a
, GnmRange
const *b
)
2551 if (a
->start
.row
== b
->start
.row
&&
2552 a
->end
.row
== b
->end
.row
&&
2553 a
->end
.col
+ 1 == b
->start
.col
) {
2554 /* "a" is just left of "b". */
2555 a
->end
.col
= b
->end
.col
;
2559 if (a
->start
.col
== b
->start
.col
&&
2560 a
->end
.col
== b
->end
.col
&&
2561 a
->end
.row
+ 1 == b
->start
.row
) {
2562 /* "a" is just on top of "b". */
2563 a
->end
.row
= b
->end
.row
;
2572 try_merge_pair (ISL
*data
, unsigned ui1
, unsigned ui2
)
2577 if (ui1
>= data
->accum
->len
|| ui2
>= data
->accum
->len
)
2580 a
= g_ptr_array_index (data
->accum
, ui1
);
2581 b
= g_ptr_array_index (data
->accum
, ui2
);
2583 if (!data
->style_equal (a
->style
, b
->style
))
2586 if (!merge_ranges (&a
->range
, &b
->range
))
2589 gnm_style_region_free (b
);
2590 g_ptr_array_remove_index (data
->accum
, ui2
);
2596 cb_style_list_add_node (GnmStyle
*style
,
2597 int corner_col
, int corner_row
, int width
, int height
,
2598 GnmRange
const *apply_to
, gpointer user_
)
2601 GnmSheetSize
const *ss
= data
->sheet_size
;
2605 /* Can this even happen? */
2606 if (corner_col
>= ss
->max_cols
|| corner_row
>= ss
->max_rows
)
2609 if (data
->style_filter
&& !data
->style_filter (style
))
2612 range
.start
.col
= corner_col
;
2613 range
.start
.row
= corner_row
;
2614 range
.end
.col
= MIN (corner_col
+ width
- 1, ss
->max_cols
- 1);
2615 range
.end
.row
= MIN (corner_row
+ height
- 1, ss
->max_rows
- 1);
2618 range
.start
.col
-= apply_to
->start
.col
;
2619 if (range
.start
.col
< 0)
2620 range
.start
.col
= 0;
2621 range
.start
.row
-= apply_to
->start
.row
;
2622 if (range
.start
.row
< 0)
2623 range
.start
.row
= 0;
2625 if (range
.end
.col
> apply_to
->end
.col
)
2626 range
.end
.col
= apply_to
->end
.col
;
2627 range
.end
.col
-= apply_to
->start
.col
;
2628 if (range
.end
.row
> apply_to
->end
.row
)
2629 range
.end
.row
= apply_to
->end
.row
;
2630 range
.end
.row
-= apply_to
->start
.row
;
2633 data
->area
+= (guint64
)range_width (&range
) * range_height (&range
);
2635 sr
= gnm_style_region_new (&range
, style
);
2636 g_ptr_array_add (data
->accum
, sr
);
2638 while (try_merge_pair (data
, data
->accum
->len
- 2, data
->accum
->len
- 1))
2643 verify_hashes (ISL
*data
)
2645 GHashTable
*by_tl
= data
->by_tl
;
2646 GHashTable
*by_br
= data
->by_br
;
2650 g_return_if_fail (g_hash_table_size (by_tl
) == data
->accum
->len
);
2651 g_return_if_fail (g_hash_table_size (by_br
) == data
->accum
->len
);
2653 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2654 GnmStyleRegion
*sr
= g_ptr_array_index (data
->accum
, ui
);
2655 g_return_if_fail (g_hash_table_lookup (by_tl
, &sr
->range
.start
) == sr
);
2656 g_return_if_fail (g_hash_table_lookup (by_br
, &sr
->range
.end
) == sr
);
2657 area
+= range_height (&sr
->range
) *
2658 (guint64
)range_width (&sr
->range
);
2661 g_return_if_fail (area
== data
->area
);
2665 merge_vertical_stripes (ISL
*data
)
2668 GHashTable
*by_tl
= data
->by_tl
;
2669 GHashTable
*by_br
= data
->by_br
;
2670 gboolean debug
= debug_style_list ();
2671 gboolean paranoid
= debug
;
2673 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2674 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2677 GSList
*Bs
= NULL
, *l
;
2678 gboolean fail
= FALSE
;
2680 /* We're looking for the setup below and extend Bs down */
2681 /* taking over part of C which is then extended to */
2682 /* include all of A. */
2686 /* +---------+ B1 | B2 | */
2688 /* +---------+----+---------+ */
2690 /* +------------------------+ */
2692 cr
.col
= a
->range
.start
.col
;
2693 cr
.row
= a
->range
.end
.row
+ 1;
2694 c
= g_hash_table_lookup (by_tl
, &cr
);
2695 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2698 cr
.col
= c
->range
.end
.col
;
2699 cr
.row
= a
->range
.end
.row
;
2700 while (cr
.col
> a
->range
.end
.col
) {
2701 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2702 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2706 Bs
= g_slist_prepend (Bs
, b
);
2707 cr
.col
= b
->range
.start
.col
- 1;
2709 if (fail
|| cr
.col
!= a
->range
.end
.col
) {
2715 g_printerr ("Vertical stripe merge:\n");
2716 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2717 for (l
= Bs
; l
; l
= l
-> next
) {
2718 GnmStyleRegion
*b
= l
->data
;
2719 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2721 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2724 g_hash_table_remove (by_tl
, &a
->range
.start
);
2725 g_hash_table_remove (by_br
, &a
->range
.end
);
2726 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2729 g_hash_table_remove (by_tl
, &c
->range
.start
);
2730 g_hash_table_remove (by_br
, &c
->range
.end
);
2731 c
->range
.start
.row
= a
->range
.start
.row
;
2732 c
->range
.end
.col
= a
->range
.end
.col
;
2733 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2734 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2736 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2738 for (l
= Bs
; l
; l
= l
-> next
) {
2739 GnmStyleRegion
*b
= l
->data
;
2740 g_hash_table_remove (by_br
, &b
->range
.end
);
2741 b
->range
.end
.row
= c
->range
.end
.row
;
2742 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2744 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2749 gnm_style_region_free (a
);
2752 if (paranoid
) verify_hashes (data
);
2757 merge_horizontal_stripes (ISL
*data
)
2760 GHashTable
*by_tl
= data
->by_tl
;
2761 GHashTable
*by_br
= data
->by_br
;
2762 gboolean debug
= debug_style_list ();
2763 gboolean paranoid
= debug
;
2765 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2766 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2769 GSList
*Bs
= NULL
, *l
;
2770 gboolean fail
= FALSE
;
2772 /* We're looking for the setup below and extend Bs right */
2773 /* taking over part of C which is then extended to */
2774 /* include all of A. */
2778 /* +----+-----+ | */
2780 /* +--+-------+ | */
2786 /* +-------+-----+ */
2788 cr
.col
= a
->range
.end
.col
+ 1;
2789 cr
.row
= a
->range
.start
.row
;
2790 c
= g_hash_table_lookup (by_tl
, &cr
);
2791 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2794 cr
.col
= a
->range
.end
.col
;
2795 cr
.row
= c
->range
.end
.row
;
2796 while (cr
.row
> a
->range
.end
.row
) {
2797 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2798 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2802 Bs
= g_slist_prepend (Bs
, b
);
2803 cr
.row
= b
->range
.start
.row
- 1;
2805 if (fail
|| cr
.row
!= a
->range
.end
.row
) {
2811 g_printerr ("Horizontal stripe merge:\n");
2812 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2813 for (l
= Bs
; l
; l
= l
-> next
) {
2814 GnmStyleRegion
*b
= l
->data
;
2815 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2817 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2820 g_hash_table_remove (by_tl
, &a
->range
.start
);
2821 g_hash_table_remove (by_br
, &a
->range
.end
);
2822 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2825 g_hash_table_remove (by_tl
, &c
->range
.start
);
2826 g_hash_table_remove (by_br
, &c
->range
.end
);
2827 c
->range
.start
.col
= a
->range
.start
.col
;
2828 c
->range
.end
.row
= a
->range
.end
.row
;
2829 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2830 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2832 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2834 for (l
= Bs
; l
; l
= l
-> next
) {
2835 GnmStyleRegion
*b
= l
->data
;
2836 g_hash_table_remove (by_br
, &b
->range
.end
);
2837 b
->range
.end
.col
= c
->range
.end
.col
;
2838 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2840 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2845 gnm_style_region_free (a
);
2848 if (paranoid
) verify_hashes (data
);
2853 by_col_row (GnmStyleRegion
**a
, GnmStyleRegion
**b
)
2857 d
= (*a
)->range
.start
.col
- (*b
)->range
.start
.col
;
2861 d
= (*a
)->range
.start
.row
- (*b
)->range
.start
.row
;
2865 static GnmStyleList
*
2866 internal_style_list (Sheet
const *sheet
, GnmRange
const *r
,
2867 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
),
2868 gboolean (*style_filter
) (GnmStyle
const *style
))
2870 GnmRange full_sheet
;
2872 GnmStyleList
*res
= NULL
;
2873 unsigned ui
, prelen
;
2874 gboolean paranoid
= FALSE
;
2877 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2880 /* This can happen if the last row or column is deleted. */
2881 if (!range_valid (r
))
2884 r
= range_init_full_sheet (&full_sheet
, sheet
);
2886 data
.accum
= g_ptr_array_new ();
2887 data
.by_tl
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2888 (GEqualFunc
)gnm_cellpos_equal
);
2889 data
.by_br
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2890 (GEqualFunc
)gnm_cellpos_equal
);
2892 data
.style_equal
= style_equal
;
2893 data
.style_filter
= style_filter
;
2894 data
.sheet_size
= gnm_sheet_get_size (sheet
);
2896 foreach_tile (sheet
, r
, cb_style_list_add_node
, &data
);
2898 sheet_area
= (guint64
)range_height (r
) * range_width (r
);
2899 if (data
.style_filter
? (data
.area
> sheet_area
) : (data
.area
!= sheet_area
))
2900 g_warning ("Strange size issue in internal_style_list");
2903 * Simple, fast optimization first. For the file underlying
2904 * bug 699045 this brings down 332688 entries to just 86.
2906 if (data
.accum
->len
>= 2) {
2907 g_ptr_array_sort (data
.accum
, (GCompareFunc
)by_col_row
);
2908 for (ui
= data
.accum
->len
- 1; ui
> 0; ui
--) {
2909 try_merge_pair (&data
, ui
- 1, ui
);
2913 /* Populate hashes. */
2914 for (ui
= 0; ui
< data
.accum
->len
; ui
++) {
2915 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2916 g_hash_table_insert (data
.by_tl
, &sr
->range
.start
, sr
);
2917 g_hash_table_insert (data
.by_br
, &sr
->range
.end
, sr
);
2920 if (paranoid
) verify_hashes (&data
);
2923 prelen
= data
.accum
->len
;
2924 merge_vertical_stripes (&data
);
2925 merge_horizontal_stripes (&data
);
2926 } while (prelen
> data
.accum
->len
);
2928 /* Always verify once. */
2929 verify_hashes (&data
);
2931 if (debug_style_list ())
2932 g_printerr ("Total of %d ranges:\n", data
.accum
->len
);
2933 for (ui
= data
.accum
->len
; ui
-- > 0; ) {
2934 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2935 if (debug_style_list ())
2936 g_printerr (" %s %p\n",
2937 range_as_string (&sr
->range
),
2939 res
= g_slist_prepend (res
, sr
);
2942 g_ptr_array_free (data
.accum
, TRUE
);
2943 g_hash_table_destroy (data
.by_tl
);
2944 g_hash_table_destroy (data
.by_br
);
2949 * sheet_style_get_range:
2950 * @sheet: the sheet in which to find styles
2951 * @r: optional range to scan
2953 * Get a list of rectangles and their associated styles.
2954 * Caller is responsible for freeing. Note that when a range is given,
2955 * the resulting ranges are relative to the input range.
2957 * Returns: (transfer full):
2960 sheet_style_get_range (Sheet
const *sheet
, GnmRange
const *r
)
2962 return internal_style_list (sheet
, r
,
2968 style_conditions_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2970 return gnm_style_get_conditions (a
) == gnm_style_get_conditions (b
);
2974 style_conditions_filter (GnmStyle
const *style
)
2976 return gnm_style_get_conditions (style
) != NULL
;
2980 * sheet_style_collect_conditions:
2984 * Returns: (transfer full): a list of areas with conditionals, Caller is
2985 * responsible for freeing.
2988 sheet_style_collect_conditions (Sheet
const *sheet
, GnmRange
const *r
)
2990 return internal_style_list (sheet
, r
,
2991 style_conditions_equal
,
2992 style_conditions_filter
);
2997 style_hlink_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2999 return gnm_style_get_hlink (a
) == gnm_style_get_hlink (b
);
3003 style_hlink_filter (GnmStyle
const *style
)
3005 return gnm_style_get_hlink (style
) != NULL
;
3009 * sheet_style_collect_hlinks:
3013 * Returns: (transfer full): a list of areas with hyperlinks, Caller is
3014 * responsible for freeing.
3017 sheet_style_collect_hlinks (Sheet
const *sheet
, GnmRange
const *r
)
3019 return internal_style_list (sheet
, r
,
3021 style_hlink_filter
);
3026 style_validation_equal (GnmStyle
const *a
, GnmStyle
const *b
)
3028 return gnm_style_get_validation (a
) == gnm_style_get_validation (b
) &&
3029 gnm_style_get_input_msg (a
) == gnm_style_get_input_msg (b
);
3033 style_validation_filter (GnmStyle
const *style
)
3035 return (gnm_style_get_validation (style
) != NULL
||
3036 gnm_style_get_input_msg (style
) != NULL
);
3040 * sheet_style_collect_validations:
3041 * @sheet: the to trawl
3042 * @r: (allow-none): range to restrict to
3044 * Returns: (transfer full): a list of areas with validation or input
3048 sheet_style_collect_validations (Sheet
const *sheet
, GnmRange
const *r
)
3050 return internal_style_list (sheet
, r
,
3051 style_validation_equal
,
3052 style_validation_filter
);
3056 * sheet_style_set_list:
3058 * @corner: The top-left corner (in LTR mode)
3060 * @range_modify: (scope call):
3063 * Overwrites the styles of the ranges given by @corner with the content of
3064 * @list. Optionally transposing the ranges
3067 sheet_style_set_list (Sheet
*sheet
, GnmCellPos
const *corner
,
3068 GnmStyleList
const *list
,
3069 sheet_style_set_list_cb_t range_modify
,
3072 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_SIMPLE
;
3073 GnmStyleList
const *l
;
3075 g_return_val_if_fail (IS_SHEET (sheet
), spanflags
);
3077 /* Sluggish but simple implementation for now */
3078 for (l
= list
; l
; l
= l
->next
) {
3079 GnmStyleRegion
const *sr
= l
->data
;
3080 GnmRange r
= sr
->range
;
3082 range_translate (&r
, sheet
, +corner
->col
, +corner
->row
);
3084 range_modify (&r
, sheet
, data
);
3086 gnm_style_ref (sr
->style
);
3087 sheet_style_set_range (sheet
, &r
, sr
->style
);
3088 spanflags
|= gnm_style_required_spanflags (sr
->style
);
3095 * @l: the list to free
3097 * Free up the ressources in the style list. Including unreferencing the
3101 style_list_free (GnmStyleList
*list
)
3103 g_slist_free_full (list
, (GDestroyNotify
)gnm_style_region_free
);
3107 * style_list_get_style:
3112 * Attempts to find the style associated with the @pos offset within the 0,0
3114 * The resulting style does not have its reference count bumped.
3117 style_list_get_style (GnmStyleList
const *list
, int col
, int row
)
3119 GnmStyleList
const *l
;
3121 for (l
= list
; l
; l
= l
->next
) {
3122 GnmStyleRegion
const *sr
= l
->data
;
3123 GnmRange
const *r
= &sr
->range
;
3124 if (range_contains (r
, col
, row
))
3131 cb_find_link (GnmStyle
*style
,
3132 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
3133 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
3134 G_GNUC_UNUSED GnmRange
const *apply_to
, gpointer user
)
3136 GnmHLink
**plink
= user
;
3138 *plink
= gnm_style_get_hlink (style
);
3142 * sheet_style_region_contains_link:
3146 * Utility routine that checks to see if a region contains at least 1 hyper link
3147 * and returns the 1st one it finds.
3149 * Returns: (transfer none): the found #GmHLink if any.
3152 sheet_style_region_contains_link (Sheet
const *sheet
, GnmRange
const *r
)
3154 GnmHLink
*res
= NULL
;
3156 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3157 g_return_val_if_fail (r
!= NULL
, NULL
);
3159 foreach_tile (sheet
, r
, cb_find_link
, &res
);
3164 * sheet_style_foreach:
3166 * @func: (scope call): callback
3167 * @user_data: user data.
3169 * Executes @func for each style in the sheet.
3172 sheet_style_foreach (Sheet
const *sheet
, GFunc func
, gpointer user_data
)
3176 g_return_if_fail (IS_SHEET (sheet
));
3177 g_return_if_fail (sheet
->style_data
!= NULL
);
3179 styles
= sh_all_styles (sheet
->style_data
->style_hash
);
3180 styles
= g_slist_sort (styles
, (GCompareFunc
)gnm_style_cmp
);
3181 g_slist_foreach (styles
, func
, user_data
);
3182 g_slist_free (styles
);
3186 * sheet_style_range_foreach:
3188 * @r: optional range
3189 * @func: (scope call): callback.
3190 * @user_data: user data
3194 sheet_style_range_foreach (Sheet
const *sheet
, GnmRange
const *r
,
3195 GHFunc func
, gpointer user_data
)
3197 GnmStyleList
*styles
, *l
;
3199 styles
= sheet_style_get_range (sheet
, r
);
3201 for (l
= styles
; l
; l
= l
->next
) {
3202 GnmStyleRegion
*sr
= l
->data
;
3204 sr
->range
.start
.col
+= r
->start
.col
;
3205 sr
->range
.start
.row
+= r
->start
.row
;
3206 sr
->range
.end
.col
+= r
->start
.col
;
3207 sr
->range
.end
.row
+= r
->start
.row
;
3209 func (NULL
, sr
, user_data
);
3210 gnm_style_region_free (sr
);
3213 g_slist_free (styles
);
3216 /* ------------------------------------------------------------------------- */
3219 cell_tile_dump (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3223 int const w
= tile_widths
[level
];
3224 int const h
= tile_heights
[level
];
3226 const char *indent
= "";
3228 type
= (*tile
)->type
;
3232 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3233 data
->ss
->max_cols
- 1),
3234 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3235 data
->ss
->max_rows
- 1));
3237 g_printerr ("%s%s: type %s\n",
3239 range_as_string (&rng
),
3240 tile_type_str
[type
]);
3242 if (type
== TILE_PTR_MATRIX
) {
3245 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3246 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3247 int c
= i
% TILE_SIZE_COL
;
3248 int r
= i
/ TILE_SIZE_COL
;
3249 cell_tile_dump (subtile
, level
- 1, data
,
3257 for (i
= 0; i
< tile_size
[type
]; i
++) {
3258 g_printerr ("%s: %d %p\n",
3261 (*tile
)->style_any
.style
[i
]);
3267 /* ------------------------------------------------------------------------- */
3270 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3274 int const w
= tile_widths
[level
];
3275 int const h
= tile_heights
[level
];
3280 type
= (*tile
)->type
;
3281 if (type
== TILE_SIMPLE
)
3286 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3287 data
->ss
->max_cols
- 1),
3288 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3289 data
->ss
->max_rows
- 1));
3294 if (!tile_is_uniform (*tile
))
3300 case TILE_PTR_MATRIX
: {
3301 gboolean all_simple
= TRUE
;
3304 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3305 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3306 if (data
->recursion
) {
3307 int c
= i
% TILE_SIZE_COL
;
3308 int r
= i
/ TILE_SIZE_COL
;
3309 cell_tile_optimize (subtile
, level
- 1, data
,
3313 if ((*subtile
)->type
!= TILE_SIMPLE
)
3319 res
= cell_tile_style_new (NULL
, TILE_MATRIX
);
3320 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3321 CellTile
*subtile
= (*tile
)->ptr_matrix
.ptr
[i
];
3322 GnmStyle
*st
= subtile
->style_simple
.style
[0];
3323 gnm_style_link (st
);
3324 res
->style_matrix
.style
[i
] = st
;
3327 if (debug_style_optimize
)
3328 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3329 range_as_string (&rng
),
3330 range_width (&rng
), range_height (&rng
),
3331 tile_type_str
[(*tile
)->type
],
3332 tile_type_str
[res
->type
]);
3333 cell_tile_dtor (*tile
);
3340 gboolean csame
= TRUE
;
3341 gboolean rsame
= TRUE
;
3344 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
3345 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
3347 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3348 (*tile
)->style_matrix
.style
[i
])) {
3354 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3355 (*tile
)->style_matrix
.style
[ c
])) {
3374 g_assert_not_reached ();
3377 if (debug_style_optimize
)
3378 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3379 range_as_string (&rng
),
3380 range_width (&rng
), range_height (&rng
),
3381 tile_type_str
[(*tile
)->type
],
3382 tile_type_str
[type
]);
3384 res
= cell_tile_style_new (NULL
, type
);
3387 res
->style_simple
.style
[0] = (*tile
)->style_any
.style
[0];
3390 for (i
= 0; i
< TILE_SIZE_ROW
; i
++)
3391 res
->style_row
.style
[i
] =
3392 (*tile
)->style_matrix
.style
[i
* TILE_SIZE_COL
];
3395 for (i
= 0; i
< TILE_SIZE_COL
; i
++)
3396 res
->style_col
.style
[i
] =
3397 (*tile
)->style_matrix
.style
[i
];
3400 g_assert_not_reached ();
3403 for (i
= 0; i
< tile_size
[type
]; i
++)
3404 gnm_style_link (res
->style_any
.style
[i
]);
3406 cell_tile_dtor (*tile
);
3411 sample_styles (Sheet
*sheet
)
3413 GnmSheetSize
const *ss
= gnm_sheet_get_size (sheet
);
3419 GnmStyle
const *mstyle
= sheet_style_get (sheet
, c
, r
);
3420 if (res
== NULL
|| !gnm_style_eq (mstyle
, res
->data
)) {
3421 gnm_style_ref (mstyle
);
3422 res
= g_slist_prepend (res
, GINT_TO_POINTER (c
));
3423 res
= g_slist_prepend (res
, GINT_TO_POINTER (r
));
3424 res
= g_slist_prepend (res
, (gpointer
)mstyle
);
3428 if (c
>= ss
->max_cols
) {
3431 if (r
>= ss
->max_rows
)
3436 return g_slist_reverse (res
);
3440 verify_styles (GSList
*pre
, GSList
*post
)
3442 GSList
*lpre
, *lpost
;
3443 gboolean silent
= FALSE
, bad
= FALSE
;
3445 for (lpre
= pre
, lpost
= post
;
3447 lpre
= (lpre
? lpre
->next
->next
->next
: NULL
),
3448 lpost
= (lpost
? lpost
->next
->next
->next
: NULL
)) {
3449 int cpre
= lpre
? GPOINTER_TO_INT (lpre
->data
) : -1;
3450 int rpre
= lpre
? GPOINTER_TO_INT (lpre
->next
->data
) : -1;
3451 GnmStyle
const *spre
= lpre
? lpre
->next
->next
->data
: NULL
;
3452 int cpost
= lpost
? GPOINTER_TO_INT (lpost
->data
) : -1;
3453 int rpost
= lpost
? GPOINTER_TO_INT (lpost
->next
->data
) : -1;
3454 GnmStyle
const *spost
= lpost
? lpost
->next
->next
->data
: NULL
;
3457 if (!spre
|| !spost
) {
3459 g_warning ("Style optimizer failure at end!");
3461 } else if (cpre
!= cpost
|| rpre
!= rpost
) {
3463 g_warning ("Style optimizer position conflict at %s!",
3464 cell_coord_name (cpre
, rpre
));
3466 } else if (!gnm_style_eq (spre
, spost
)) {
3468 g_warning ("Style optimizer failure at %s!",
3469 cell_coord_name (cpre
, rpre
));
3473 if (spre
) gnm_style_unref (spre
);
3474 if (spost
) gnm_style_unref (spost
);
3478 g_slist_free (post
);
3484 sheet_style_optimize (Sheet
*sheet
)
3486 CellTileOptimize data
;
3490 g_return_if_fail (IS_SHEET (sheet
));
3492 if (gnm_debug_flag ("no-style-optimize"))
3495 sheet_colrow_optimize (sheet
);
3497 data
.ss
= gnm_sheet_get_size (sheet
);
3498 data
.recursion
= TRUE
;
3500 if (debug_style_optimize
) {
3501 g_printerr ("Optimizing %s\n", sheet
->name_unquoted
);
3502 cell_tile_dump (&sheet
->style_data
->styles
,
3503 sheet
->tile_top_level
, &data
,
3507 verify
= gnm_debug_flag ("style-optimize-verify");
3508 pre
= verify
? sample_styles (sheet
) : NULL
;
3510 cell_tile_optimize (&sheet
->style_data
->styles
,
3511 sheet
->tile_top_level
, &data
,
3514 if (debug_style_optimize
)
3515 g_printerr ("Optimizing %s...done\n", sheet
->name_unquoted
);
3518 GSList
*post
= sample_styles (sheet
);
3519 verify_styles (pre
, post
);