1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * sheet-style.c: storage mechanism for styles and eventually cells.
5 * Copyright (C) 2000-2006 Jody Goldberg (jody@gnome.org)
6 * Copyright 2013 Morten Welinder (terra@gnome.org)
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) version 3.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
24 #include <gnumeric-config.h>
25 #include "sheet-style.h"
30 #include "style-border.h"
31 #include "style-color.h"
32 #include "style-conditions.h"
33 #include "parse-util.h"
36 #include <goffice/goffice.h>
37 #include <glib/gi18n-lib.h>
41 #define USE_TILE_POOLS 0
43 /* ------------------------------------------------------------------------- */
46 * This is, essentially, an std::multiset implementation for the style hash.
47 * Note, however, that sh_lookup is based on gnm_style_equal, not gnm_style_eq.
49 typedef GHashTable GnmStyleHash
;
52 /* This is a really crummy hash -- except for forcing collisions. */
53 #define gnm_style_hash(st) 0
57 sh_remove (GnmStyleHash
*h
, GnmStyle
*st
)
59 guint32 hv
= gnm_style_hash (st
);
60 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
62 g_return_if_fail (l
!= NULL
);
65 GSList
*next
= l
->next
;
67 /* We're removing the first of several elements. */
69 g_hash_table_replace (h
, GUINT_TO_POINTER (hv
), next
);
71 /* We're removing the last element. */
72 g_hash_table_remove (h
, GUINT_TO_POINTER (hv
));
75 /* We're removing an element that isn't first. */
76 l
= g_slist_remove (l
, st
);
81 sh_lookup (GnmStyleHash
*h
, GnmStyle
*st
)
83 guint32 hv
= gnm_style_hash (st
);
84 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
86 GnmStyle
*st2
= l
->data
;
87 /* NOTE: This uses gnm_style_equal, not gnm_style_eq. */
88 if (gnm_style_equal (st
, st2
))
96 sh_insert (GnmStyleHash
*h
, GnmStyle
*st
)
98 GSList
*s
= g_slist_prepend (NULL
, st
);
99 guint32 hv
= gnm_style_hash (st
);
100 GSList
*l
= g_hash_table_lookup (h
, GUINT_TO_POINTER (hv
));
105 g_hash_table_insert (h
, GUINT_TO_POINTER (hv
), s
);
110 sh_all_styles (GnmStyleHash
*h
)
116 g_hash_table_iter_init (&iter
, h
);
117 while (g_hash_table_iter_next (&iter
, NULL
, &value
)) {
119 for (; l
; l
= l
->next
)
120 res
= g_slist_prepend (res
, l
->data
);
126 static GnmStyleHash
*
129 return g_hash_table_new_full (g_direct_hash
, g_direct_equal
,
130 NULL
, (GDestroyNotify
)g_slist_free
);
134 sh_destroy (GnmStyleHash
*h
)
136 g_hash_table_destroy (h
);
139 /* ------------------------------------------------------------------------- */
141 typedef union _CellTile CellTile
;
142 struct _GnmSheetStyleData
{
144 * style_hash is a set of all styles used by this sheet. These
145 * styles are all linked.
147 * We always re-use styles from here when we can, but there can
148 * still be duplicates. This happens when styles are changed
149 * while they are in the hash. For example, this happens when
150 * an expression used by a validation style changes due to
151 * row/col insert/delete.
153 GnmStyleHash
*style_hash
;
156 GnmStyle
*default_style
;
157 GnmColor
*auto_pattern_color
;
160 static gboolean debug_style_optimize
;
163 GnmSheetSize
const *ss
;
168 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
174 * For internal use only
177 sheet_style_unlink (Sheet
*sheet
, GnmStyle
*st
)
179 if (sheet
->style_data
->style_hash
)
180 sh_remove (sheet
->style_data
->style_hash
, st
);
185 * @sheet: (transfer full): the sheet
188 * Looks up a style from the sheets collection. Linking if necessary.
189 * ABSORBS the reference and adds a link.
191 * Returns: (transfer full): the new style.
194 sheet_style_find (Sheet
const *sheet
, GnmStyle
*s
)
197 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
199 gnm_style_link (res
);
204 s
= gnm_style_link_sheet (s
, (Sheet
*)sheet
);
206 /* Retry the lookup in case "s" changed. See #585178. */
207 res
= sh_lookup (sheet
->style_data
->style_hash
, s
);
209 gnm_style_link (res
);
211 * We are abandoning the linking here. We cannot use
212 * gnm_style_unlink as that would call sheet_style_unlink
213 * and thus remove "res" from the hash.
215 gnm_style_abandon_link (s
);
221 sh_insert (sheet
->style_data
->style_hash
, s
);
225 /* Place holder until I merge in the new styles too */
227 pstyle_set_border (GnmStyle
*st
, GnmBorder
*border
,
228 GnmStyleBorderLocation side
)
230 gnm_style_set_border (st
,
231 GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (side
),
232 gnm_style_border_ref (border
));
235 /* Amortize the cost of applying a partial style over a large region
236 * by caching and rereferencing the merged result for repeated styles.
246 rstyle_ctor_style (ReplacementStyle
*res
, GnmStyle
*new_style
, Sheet
*sheet
)
249 res
->new_style
= sheet_style_find (sheet
, new_style
);
255 rstyle_ctor_pstyle (ReplacementStyle
*res
, GnmStyle
*pstyle
, Sheet
*sheet
)
258 res
->new_style
= NULL
;
259 res
->pstyle
= pstyle
;
260 res
->cache
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
264 cb_style_unlink (gpointer key
, gpointer value
, G_GNUC_UNUSED gpointer user_data
)
266 gnm_style_unlink ((GnmStyle
*)key
);
267 gnm_style_unlink ((GnmStyle
*)value
);
271 rstyle_dtor (ReplacementStyle
*rs
)
273 if (rs
->cache
!= NULL
) {
274 g_hash_table_foreach (rs
->cache
, cb_style_unlink
, NULL
);
275 g_hash_table_destroy (rs
->cache
);
278 if (rs
->new_style
!= NULL
) {
279 gnm_style_unlink (rs
->new_style
);
280 rs
->new_style
= NULL
;
282 if (rs
->pstyle
!= NULL
) {
283 gnm_style_unref (rs
->pstyle
);
289 * rstyle_apply: Utility routine that is at the core of applying partial
290 * styles or storing complete styles. It will eventually be smarter
291 * and will maintain the cache of styles associated with each sheet
294 rstyle_apply (GnmStyle
**old
, ReplacementStyle
*rs
, GnmRange
const *r
)
297 g_return_if_fail (old
!= NULL
);
298 g_return_if_fail (rs
!= NULL
);
300 if (rs
->pstyle
!= NULL
) {
301 /* Cache the merged styles keeping a reference to the originals
302 * just in case all instances change.
304 s
= g_hash_table_lookup (rs
->cache
, *old
);
306 GnmStyle
*tmp
= gnm_style_new_merged (*old
, rs
->pstyle
);
307 s
= sheet_style_find (rs
->sheet
, tmp
);
308 gnm_style_link (*old
);
309 g_hash_table_insert (rs
->cache
, *old
, s
);
316 gnm_style_unlink_dependents (*old
, r
);
317 gnm_style_unlink (*old
);
320 gnm_style_link_dependents (s
, r
);
328 sheet_style_clear_style_dependents (Sheet
*sheet
, GnmRange
const *r
)
330 GSList
*styles
= sh_all_styles (sheet
->style_data
->style_hash
);
331 g_slist_foreach (styles
,
332 (GFunc
)gnm_style_unlink_dependents
,
334 g_slist_free (styles
);
338 /****************************************************************************/
340 /* If you change this, change the tile_{widths,heights} here
341 * and GNM_MAX_COLS and GNM_MAX_ROWS in gnumeric.h */
342 #define TILE_TOP_LEVEL 6
344 #define TILE_SIZE_COL 8
345 #define TILE_SIZE_ROW 16
355 static int const tile_size
[/*type*/] = {
357 TILE_SIZE_COL
, /* TILE_COL */
358 TILE_SIZE_ROW
, /* TILE_ROW */
359 TILE_SIZE_COL
* TILE_SIZE_ROW
/* TILE_MATRIX */
361 static int const tile_col_count
[/*type*/] = {
363 TILE_SIZE_COL
, /* TILE_COL */
365 TILE_SIZE_COL
, /* TILE_MATRIX */
366 TILE_SIZE_COL
/* TILE_PTR_MATRIX */
368 static int const tile_row_count
[/*type*/] = {
371 TILE_SIZE_ROW
, /* TILE_ROW */
372 TILE_SIZE_ROW
, /* TILE_MATRIX */
373 TILE_SIZE_ROW
/* TILE_PTR_MATRIX */
375 static const char * const tile_type_str
[/*type*/] = {
376 "simple", "col", "row", "matrix", "ptr-matrix"
378 static int const tile_widths
[/*level*/] = {
381 TILE_SIZE_COL
* TILE_SIZE_COL
,
382 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
383 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
,
385 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
,
386 TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
* TILE_SIZE_COL
388 static int const tile_heights
[/*level*/] = {
391 TILE_SIZE_ROW
* TILE_SIZE_ROW
,
392 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
393 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
,
395 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
,
396 TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
* TILE_SIZE_ROW
400 CellTileType
const type
;
402 } CellTileStyleSimple
;
404 CellTileType
const type
;
405 GnmStyle
*style
[TILE_SIZE_COL
];
408 CellTileType
const type
;
409 GnmStyle
*style
[TILE_SIZE_ROW
];
412 CellTileType
const type
;
413 GnmStyle
*style
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
414 } CellTileStyleMatrix
;
416 CellTileType
const type
;
417 CellTile
*ptr
[TILE_SIZE_COL
* TILE_SIZE_ROW
];
421 CellTileType
const type
;
422 CellTileStyleSimple style_any
;
423 CellTileStyleSimple style_simple
;
424 CellTileStyleCol style_col
;
425 CellTileStyleRow style_row
;
426 CellTileStyleMatrix style_matrix
;
427 CellTilePtrMatrix ptr_matrix
;
430 static int active_sheet_count
;
432 static GOMemChunk
*tile_pools
[5];
433 #define CHUNK_ALLOC(T,ctt) ((T*)go_mem_chunk_alloc (tile_pools[(ctt)]))
434 #define CHUNK_FREE(ctt,v) go_mem_chunk_free (tile_pools[(ctt)], (v))
436 static const size_t tile_type_sizeof
[5] = {
437 sizeof (CellTileStyleSimple
),
438 sizeof (CellTileStyleCol
),
439 sizeof (CellTileStyleRow
),
440 sizeof (CellTileStyleMatrix
),
441 sizeof (CellTilePtrMatrix
)
443 static int tile_allocations
= 0;
445 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_slice_alloc (tile_type_sizeof[(ctt)]))
446 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_slice_free1 (tile_type_sizeof[(ctt)], (v)))
448 #define CHUNK_ALLOC(T,ctt) (tile_allocations++, (T*)g_malloc (tile_type_sizeof[(ctt)]))
449 #define CHUNK_FREE(ctt,v) (tile_allocations--, g_free ((v)))
455 * Destroy a CellTile (recursively if needed). This will unlink all the
456 * styles in it. We do _not_ unlink style dependents here. That is done
457 * only in rstyle_apply.
460 cell_tile_dtor (CellTile
*tile
)
464 g_return_if_fail (tile
!= NULL
);
467 if (t
== TILE_PTR_MATRIX
) {
468 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
470 cell_tile_dtor (tile
->ptr_matrix
.ptr
[i
]);
471 tile
->ptr_matrix
.ptr
[i
] = NULL
;
473 } else if (TILE_SIMPLE
<= t
&& t
<= TILE_MATRIX
) {
474 int i
= tile_size
[t
];
476 gnm_style_unlink (tile
->style_any
.style
[i
]);
477 tile
->style_any
.style
[i
] = NULL
;
480 g_return_if_fail (FALSE
); /* don't free anything */
483 *((CellTileType
*)&(tile
->type
)) = TILE_UNDEFINED
; /* poison it */
484 CHUNK_FREE (t
, tile
);
488 cell_tile_style_new (GnmStyle
*style
, CellTileType t
)
490 CellTile
*res
= CHUNK_ALLOC (CellTile
, t
);
491 *((CellTileType
*)&(res
->type
)) = t
;
494 int i
= tile_size
[t
];
495 gnm_style_link_multiple (style
, i
);
497 res
->style_any
.style
[i
] = style
;
504 cell_tile_ptr_matrix_new (CellTile
*t
)
506 CellTilePtrMatrix
*res
;
508 g_return_val_if_fail (t
!= NULL
, NULL
);
509 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
510 TILE_MATRIX
>= t
->type
, NULL
);
512 res
= CHUNK_ALLOC (CellTilePtrMatrix
, TILE_PTR_MATRIX
);
513 *((CellTileType
*)&(res
->type
)) = TILE_PTR_MATRIX
;
516 * If we wanted to get fancy we could use self similarity to decrease
517 * the number of subtiles. However, this would increase the cost of
518 * applying changes later so I'm not sure it is worth the effort.
522 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
524 res
->ptr
[i
] = cell_tile_style_new (
525 t
->style_simple
.style
[0], TILE_SIMPLE
);
530 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
531 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
532 res
->ptr
[i
++] = cell_tile_style_new (
533 t
->style_col
.style
[c
], TILE_SIMPLE
);
538 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
)
539 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
)
540 res
->ptr
[i
++] = cell_tile_style_new (
541 t
->style_row
.style
[r
], TILE_SIMPLE
);
545 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
547 res
->ptr
[i
] = cell_tile_style_new (
548 t
->style_matrix
.style
[i
], TILE_SIMPLE
);
554 return (CellTile
*)res
;
558 cell_tile_matrix_set (CellTile
*t
)
561 CellTileStyleMatrix
*res
;
563 g_return_val_if_fail (t
!= NULL
, NULL
);
564 g_return_val_if_fail (TILE_SIMPLE
<= t
->type
&&
565 TILE_MATRIX
>= t
->type
, NULL
);
567 if (t
->type
== TILE_MATRIX
)
570 res
= (CellTileStyleMatrix
*)cell_tile_style_new (NULL
, TILE_MATRIX
);
574 GnmStyle
*tmp
= t
->style_simple
.style
[0];
575 int i
= TILE_SIZE_COL
* TILE_SIZE_ROW
;
576 gnm_style_link_multiple (tmp
, i
);
584 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
)
585 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
586 gnm_style_link (res
->style
[i
++] =
587 t
->style_col
.style
[c
]);
593 for (r
= 0; r
< TILE_SIZE_ROW
; ++r
) {
594 GnmStyle
*tmp
= t
->style_row
.style
[r
];
595 gnm_style_link_multiple (tmp
, TILE_SIZE_COL
);
596 for (c
= 0; c
< TILE_SIZE_COL
; ++c
)
597 res
->style
[i
++] = tmp
;
604 g_assert_not_reached();
609 return (CellTile
*)res
;
612 /****************************************************************************/
615 sheet_style_sanity_check (void)
620 for (c
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
621 g_assert (c
< G_MAXUINT
/ TILE_SIZE_COL
);
624 g_assert (c
>= GNM_MAX_COLS
);
626 for (r
= 1, i
= 0; i
<= TILE_TOP_LEVEL
; i
++) {
627 g_assert (r
< G_MAXUINT
/ TILE_SIZE_COL
);
630 g_assert (r
>= GNM_MAX_ROWS
);
632 g_assert (G_N_ELEMENTS (tile_heights
) > TILE_TOP_LEVEL
+ 1);
634 g_assert (G_N_ELEMENTS (tile_widths
) > TILE_TOP_LEVEL
+ 1);
638 sheet_style_init_size (Sheet
*sheet
, int cols
, int rows
)
640 GnmStyle
*default_style
;
641 int lc
= 0, lr
= 0, w
= TILE_SIZE_COL
, h
= TILE_SIZE_ROW
;
651 sheet
->tile_top_level
= MAX (lc
, lr
);
653 if (active_sheet_count
++ == 0) {
655 tile_pools
[TILE_SIMPLE
] =
656 go_mem_chunk_new ("simple tile pool",
657 sizeof (CellTileStyleSimple
),
659 tile_pools
[TILE_COL
] =
660 go_mem_chunk_new ("column tile pool",
661 sizeof (CellTileStyleCol
),
663 tile_pools
[TILE_ROW
] =
664 go_mem_chunk_new ("row tile pool",
665 sizeof (CellTileStyleRow
),
667 tile_pools
[TILE_MATRIX
] =
668 go_mem_chunk_new ("matrix tile pool",
669 sizeof (CellTileStyleMatrix
),
670 MAX (16 * 1024 - 128,
671 100 * sizeof (CellTileStyleMatrix
)));
673 /* If this fails one day, just make two pools. */
674 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
675 tile_pools
[TILE_PTR_MATRIX
] = tile_pools
[TILE_MATRIX
];
679 sheet
->style_data
= g_new (GnmSheetStyleData
, 1);
680 sheet
->style_data
->style_hash
= sh_create ();
682 sheet
->style_data
->auto_pattern_color
= style_color_auto_pattern ();
684 default_style
= gnm_style_new_default ();
686 /* We can not do this, XL creates full page charts with background
687 * 'none' by default. Then displays that as white. */
688 if (sheet
->sheet_type
== GNM_SHEET_OBJECT
) {
689 gnm_style_set_back_color (default_style
,
690 gnm_color_new_rgb8 (0x50, 0x50, 0x50));
691 gnm_style_set_pattern (default_style
, 1);
694 sheet
->style_data
->default_style
=
695 sheet_style_find (sheet
, default_style
);
696 sheet
->style_data
->styles
=
697 cell_tile_style_new (sheet
->style_data
->default_style
,
702 sheet_style_init (Sheet
*sheet
)
704 int cols
= gnm_sheet_get_max_cols (sheet
);
705 int rows
= gnm_sheet_get_max_rows (sheet
);
707 debug_style_optimize
= gnm_debug_flag ("style-optimize");
709 sheet_style_sanity_check ();
711 sheet_style_init_size (sheet
, cols
, rows
);
715 sheet_style_resize (Sheet
*sheet
, int cols
, int rows
)
717 GnmStyleList
*styles
, *l
;
718 int old_cols
= gnm_sheet_get_max_cols (sheet
);
719 int old_rows
= gnm_sheet_get_max_rows (sheet
);
720 GnmRange save_range
, new_full
;
722 /* Save the style for the surviving area. */
723 range_init (&save_range
, 0, 0,
724 MIN (cols
, old_cols
) - 1, MIN (rows
, old_rows
) - 1);
725 styles
= sheet_style_get_range (sheet
, &save_range
);
727 /* Build new empty structures. */
728 sheet_style_shutdown (sheet
);
729 sheet_style_init_size (sheet
, cols
, rows
);
731 /* Reapply styles. */
732 range_init (&new_full
, 0, 0, cols
- 1, rows
- 1);
733 for (l
= styles
; l
; l
= l
->next
) {
734 GnmStyleRegion
const *sr
= l
->data
;
735 GnmRange
const *r
= &sr
->range
;
736 GnmStyle
*style
= sr
->style
;
738 if (range_intersection (&newr
, r
, &new_full
))
739 sheet_style_apply_range2 (sheet
, &newr
, style
);
742 style_list_free (styles
);
747 cb_tile_pool_leak (gpointer data
, gpointer user
)
749 CellTile
*tile
= data
;
750 g_printerr ("Leaking tile at %p.\n", (void *)tile
);
755 sheet_style_shutdown (Sheet
*sheet
)
760 g_return_if_fail (IS_SHEET (sheet
));
761 g_return_if_fail (sheet
->style_data
!= NULL
);
764 * Clear all styles. This is an easy way to clear out all
765 * style dependencies.
767 range_init_full_sheet (&r
, sheet
);
768 sheet_style_set_range (sheet
, &r
, sheet_style_default (sheet
));
770 cell_tile_dtor (sheet
->style_data
->styles
);
771 sheet
->style_data
->styles
= NULL
;
773 sheet
->style_data
->default_style
= NULL
;
775 /* Clear the pointer to the hash BEFORE clearing and add a test in
776 * sheet_style_unlink. If we don't then it is possible/probable that
777 * unlinking the styles will attempt to remove them from the hash while
780 table
= sheet
->style_data
->style_hash
;
781 sheet
->style_data
->style_hash
= NULL
;
782 g_slist_free_full (sh_all_styles (table
),
783 (GDestroyNotify
)gnm_style_unlink
);
785 style_color_unref (sheet
->style_data
->auto_pattern_color
);
787 g_free (sheet
->style_data
);
788 sheet
->style_data
= NULL
;
790 if (--active_sheet_count
== 0) {
792 go_mem_chunk_foreach_leak (tile_pools
[TILE_SIMPLE
],
793 cb_tile_pool_leak
, NULL
);
794 go_mem_chunk_destroy (tile_pools
[TILE_SIMPLE
], FALSE
);
795 tile_pools
[TILE_SIMPLE
] = NULL
;
797 go_mem_chunk_foreach_leak (tile_pools
[TILE_COL
],
798 cb_tile_pool_leak
, NULL
);
799 go_mem_chunk_destroy (tile_pools
[TILE_COL
], FALSE
);
800 tile_pools
[TILE_COL
] = NULL
;
802 go_mem_chunk_foreach_leak (tile_pools
[TILE_ROW
],
803 cb_tile_pool_leak
, NULL
);
804 go_mem_chunk_destroy (tile_pools
[TILE_ROW
], FALSE
);
805 tile_pools
[TILE_ROW
] = NULL
;
807 go_mem_chunk_foreach_leak (tile_pools
[TILE_MATRIX
],
808 cb_tile_pool_leak
, NULL
);
809 go_mem_chunk_destroy (tile_pools
[TILE_MATRIX
], FALSE
);
810 tile_pools
[TILE_MATRIX
] = NULL
;
812 /* If this fails one day, just make two pools. */
813 g_assert (sizeof (CellTileStyleMatrix
) == sizeof (CellTilePtrMatrix
));
814 tile_pools
[TILE_PTR_MATRIX
] = NULL
;
816 if (tile_allocations
)
817 g_printerr ("Leaking %d style tiles.\n", tile_allocations
);
823 * sheet_style_set_auto_pattern_color:
825 * @grid_color: (transfer full): The color
827 * Set the color for rendering auto colored patterns in this sheet.
828 * Absorbs a reference to @pattern_color;
831 sheet_style_set_auto_pattern_color (Sheet
*sheet
, GnmColor
*pattern_color
)
833 g_return_if_fail (IS_SHEET (sheet
));
834 g_return_if_fail (sheet
->style_data
!= NULL
);
836 style_color_unref (sheet
->style_data
->auto_pattern_color
);
837 sheet
->style_data
->auto_pattern_color
= gnm_color_new_auto (pattern_color
->go_color
);
838 style_color_unref (pattern_color
);
842 * sheet_style_get_auto_pattern_color:
845 * Returns: (transfer full): the color for rendering auto colored patterns
849 sheet_style_get_auto_pattern_color (Sheet
const *sheet
)
852 g_return_val_if_fail (IS_SHEET (sheet
), style_color_black ());
853 g_return_val_if_fail (sheet
->style_data
!= NULL
, style_color_black ());
854 g_return_val_if_fail (sheet
->style_data
->auto_pattern_color
!= NULL
,
855 style_color_black ());
857 sc
= sheet
->style_data
->auto_pattern_color
;
858 style_color_ref (sc
);
864 * sheet_style_update_grid_color:
866 * This function updates the color of gnm_style_border_none when the sheet to be
867 * rendered is known. gnm_style_border_none tells how to render the
868 * grid. Because the grid color may be different for different sheets, the
869 * functions which render the grid call this function first. The rule for
870 * selecting the grid color, which is the same as in Excel, is: - if the
871 * auto pattern color is default (which is black), the grid color is gray,
872 * as returned by style_color_grid (). - otherwise, the auto pattern color
873 * is used for the grid.
876 sheet_style_update_grid_color (Sheet
const *sheet
)
878 GnmColor
*default_auto
= style_color_auto_pattern ();
879 GnmColor
*sheet_auto
= sheet_style_get_auto_pattern_color (sheet
);
880 GnmColor
*grid_color
= style_color_grid ();
883 new_color
= (style_color_equal (default_auto
, sheet_auto
)
884 ? grid_color
: sheet_auto
);
886 /* Do nothing if we already have the right color */
887 if (gnm_style_border_none()->color
!= new_color
) {
888 style_color_ref (new_color
); /* none_set eats the ref */
889 gnm_style_border_none_set_color (new_color
);
891 style_color_unref (grid_color
);
892 style_color_unref (sheet_auto
);
893 style_color_unref (default_auto
);
896 /****************************************************************************/
899 tile_is_uniform (CellTile
const *tile
)
901 const int s
= tile_size
[tile
->type
];
902 GnmStyle
const *st
= tile
->style_any
.style
[0];
905 for (i
= 1; i
< s
; i
++)
906 if (tile
->style_any
.style
[i
] != st
)
913 vector_apply_pstyle (CellTile
*tile
, ReplacementStyle
*rs
,
914 int cc
, int cr
, int level
, GnmRange
const *indic
)
916 const CellTileType type
= tile
->type
;
917 const int ncols
= tile_col_count
[type
];
918 const int nrows
= tile_row_count
[type
];
919 const int w1
= tile_widths
[level
+ 1] / ncols
;
920 const int h1
= tile_heights
[level
+ 1] / nrows
;
921 const int fcol
= indic
->start
.col
;
922 const int frow
= indic
->start
.row
;
923 const int lcol
= MIN (ncols
- 1, indic
->end
.col
);
924 const int lrow
= MIN (nrows
- 1, indic
->end
.row
);
925 GnmSheetSize
const *ss
= gnm_sheet_get_size (rs
->sheet
);
929 for (r
= frow
; r
<= lrow
; r
++) {
930 GnmStyle
**st
= tile
->style_any
.style
+ ncols
* r
;
931 rng
.start
.row
= cr
+ h1
* r
;
932 rng
.end
.row
= MIN (rng
.start
.row
+ (h1
- 1),
934 for (c
= fcol
; c
<= lcol
; c
++) {
935 rng
.start
.col
= cc
+ w1
* c
;
936 rng
.end
.col
= MIN (rng
.start
.col
+ (w1
- 1),
938 rstyle_apply (st
+ c
, rs
, &rng
);
944 * Determine whether before applying a style in the area of apply_to
945 * one needs to split the tile column-wise.
947 * If FALSE is returned then the tile need to be split to a TILE_PTR_MATRIX
948 * because the current level is not fine-grained enough.
950 * If TRUE is returned, TILE_SIMPLE needs to be split into TILE_COL and
951 * TILE_ROW needs to be split into TILE_MATRIX. TILE_COL and TILE_MATRIX
952 * should be kept. In indic, the inclusive post-split indicies of the
953 * range will be returned.
955 * If apply_to covers the entire tile, TRUE will be returned and the judgement
956 * on splitting above should be ignored. The indices in indic will be as-if
957 * the split was done.
960 col_indicies (int corner_col
, int w
, GnmRange
const *apply_to
,
965 i
= apply_to
->start
.col
- corner_col
;
967 indec
->start
.col
= 0;
972 indec
->start
.col
= tmp
;
975 i
= 1 + apply_to
->end
.col
- corner_col
;
977 if (tmp
>= TILE_SIZE_COL
)
978 indec
->end
.col
= TILE_SIZE_COL
- 1;
982 indec
->end
.col
= tmp
- 1;
988 /* See docs for col_indicies. Swap cols and rows. */
990 row_indicies (int corner_row
, int h
, GnmRange
const *apply_to
,
995 i
= apply_to
->start
.row
- corner_row
;
997 indic
->start
.row
= 0;
1002 indic
->start
.row
= tmp
;
1005 i
= 1 + apply_to
->end
.row
- corner_row
;
1007 if (tmp
>= TILE_SIZE_ROW
)
1008 indic
->end
.row
= TILE_SIZE_ROW
- 1;
1012 indic
->end
.row
= tmp
- 1;
1019 * cell_tile_apply: This is the primary logic for making changing areas in the
1020 * tree. It could be further optimised if it becomes a bottle neck.
1023 cell_tile_apply (CellTile
**tile
, int level
,
1024 int corner_col
, int corner_row
,
1025 GnmRange
const *apply_to
,
1026 ReplacementStyle
*rs
)
1028 int const width
= tile_widths
[level
+1];
1029 int const height
= tile_heights
[level
+1];
1030 int const w
= tile_widths
[level
];
1031 int const h
= tile_heights
[level
];
1032 gboolean
const full_width
= (apply_to
->start
.col
<= corner_col
&&
1033 apply_to
->end
.col
>= (corner_col
+width
-1));
1034 gboolean
const full_height
= (apply_to
->start
.row
<= corner_row
&&
1035 apply_to
->end
.row
>= (corner_row
+height
-1));
1040 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1041 g_return_if_fail (tile
!= NULL
);
1042 g_return_if_fail (*tile
!= NULL
);
1044 type
= (*tile
)->type
;
1045 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1047 /* applying the same style to part of a simple-tile is a nop */
1048 if (type
== TILE_SIMPLE
&&
1049 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1053 * Indices for the whole tile assuming a split to matrix.
1054 * We can still use these indices if we don't split either way.
1056 indic
.start
.col
= 0;
1057 indic
.start
.row
= 0;
1058 indic
.end
.col
= TILE_SIZE_COL
- 1;
1059 indic
.end
.row
= TILE_SIZE_ROW
- 1;
1061 if (type
== TILE_PTR_MATRIX
)
1063 else if (full_width
&& full_height
)
1065 else if (full_height
) {
1066 if (!col_indicies (corner_col
, w
, apply_to
, &indic
))
1067 goto split_to_ptr_matrix
;
1073 res
= cell_tile_style_new (
1074 (*tile
)->style_simple
.style
[0],
1076 cell_tile_dtor (*tile
);
1084 goto split_to_matrix
;
1086 g_assert_not_reached ();
1088 } else if (full_width
) {
1089 if (!row_indicies (corner_row
, h
, apply_to
, &indic
))
1090 goto split_to_ptr_matrix
;
1096 res
= cell_tile_style_new (
1097 (*tile
)->style_simple
.style
[0],
1099 cell_tile_dtor (*tile
);
1107 goto split_to_matrix
;
1109 g_assert_not_reached ();
1112 if (col_indicies (corner_col
, w
, apply_to
, &indic
) &&
1113 row_indicies (corner_row
, h
, apply_to
, &indic
))
1114 goto split_to_matrix
;
1116 goto split_to_ptr_matrix
;
1119 g_assert_not_reached ();
1122 *tile
= cell_tile_matrix_set (*tile
);
1125 vector_apply_pstyle (*tile
, rs
, corner_col
, corner_row
, level
, &indic
);
1129 CellTileOptimize cto
;
1130 cto
.ss
= gnm_sheet_get_size (rs
->sheet
);
1131 cto
.recursion
= FALSE
;
1132 cell_tile_optimize (tile
, level
, &cto
, corner_col
, corner_row
);
1136 split_to_ptr_matrix
:
1138 * We get here when apply_to's corners are not on a TILE_MATRIX grid.
1139 * Split to pointer matrix whose element tiles will have a finer grid.
1141 g_return_if_fail (type
!= TILE_PTR_MATRIX
);
1143 CellTile
*res
= cell_tile_ptr_matrix_new (*tile
);
1144 cell_tile_dtor (*tile
);
1146 type
= TILE_PTR_MATRIX
;
1150 g_return_if_fail (type
== TILE_PTR_MATRIX
);
1151 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1152 int const cr
= corner_row
+ h
*r
;
1153 if (cr
> apply_to
->end
.row
)
1155 if ((cr
+ h
) <= apply_to
->start
.row
)
1158 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1159 int const cc
= corner_col
+ w
*c
;
1160 if (cc
> apply_to
->end
.col
)
1162 if ((cc
+ w
) <= apply_to
->start
.col
)
1165 cell_tile_apply ((*tile
)->ptr_matrix
.ptr
+ i
+ c
,
1166 level
- 1, cc
, cr
, apply_to
, rs
);
1172 /* Handler for foreach_tile.
1174 * "width" and "height" refer to tile size which may extend beyond
1175 * the range supplied to foreach_tile and even beyond the sheet.
1177 typedef void (*ForeachTileFunc
) (GnmStyle
*style
,
1178 int corner_col
, int corner_row
,
1179 int width
, int height
,
1180 GnmRange
const *apply_to
, gpointer user
);
1182 foreach_tile_r (CellTile
*tile
, int level
,
1183 int corner_col
, int corner_row
,
1184 GnmRange
const *apply_to
,
1185 ForeachTileFunc handler
,
1188 int const width
= tile_widths
[level
+1];
1189 int const height
= tile_heights
[level
+1];
1190 int const w
= tile_widths
[level
];
1191 int const h
= tile_heights
[level
];
1194 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1195 g_return_if_fail (tile
!= NULL
);
1197 switch (tile
->type
) {
1199 handler (tile
->style_simple
.style
[0],
1200 corner_col
, corner_row
, width
, height
,
1205 if (apply_to
!= NULL
) {
1206 c
= (apply_to
->start
.col
- corner_col
) / w
;
1209 last
= (apply_to
->end
.col
- corner_col
) / w
+ 1;
1210 if (last
> TILE_SIZE_COL
)
1211 last
= TILE_SIZE_COL
;
1214 last
= TILE_SIZE_COL
;
1216 for (; c
< last
; ++c
)
1217 handler (tile
->style_col
.style
[c
],
1218 corner_col
+ c
*w
, corner_row
, w
, height
,
1223 if (apply_to
!= NULL
) {
1224 r
= (apply_to
->start
.row
- corner_row
) / h
;
1227 last
= (apply_to
->end
.row
- corner_row
) / h
+ 1;
1228 if (last
> TILE_SIZE_ROW
)
1229 last
= TILE_SIZE_ROW
;
1232 last
= TILE_SIZE_ROW
;
1234 for (; r
< last
; ++r
)
1235 handler (tile
->style_row
.style
[r
],
1236 corner_col
, corner_row
+ r
*h
, width
, h
,
1241 case TILE_PTR_MATRIX
:
1242 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
1243 int const cr
= corner_row
+ h
*r
;
1245 if (cr
> apply_to
->end
.row
)
1247 if ((cr
+ h
) <= apply_to
->start
.row
)
1251 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
1252 int const cc
= corner_col
+ w
*c
;
1254 if (cc
> apply_to
->end
.col
)
1256 if ((cc
+ w
) <= apply_to
->start
.col
)
1260 if (tile
->type
== TILE_MATRIX
) {
1261 handler (tile
->style_matrix
.style
[r
*TILE_SIZE_COL
+c
],
1264 w
, h
, apply_to
, user
);
1267 tile
->ptr_matrix
.ptr
[c
+ r
*TILE_SIZE_COL
],
1268 level
-1, cc
, cr
, apply_to
, handler
, user
);
1275 g_warning ("Adaptive Quad Tree corruption !");
1280 foreach_tile (Sheet
const *sheet
, GnmRange
const *apply_to
,
1281 ForeachTileFunc handler
, gpointer user
)
1283 foreach_tile_r (sheet
->style_data
->styles
,
1284 sheet
->tile_top_level
, 0, 0,
1285 apply_to
, handler
, user
);
1289 * cell_tile_apply_pos: This is an simplified version of cell_tile_apply. It
1290 * does not need all the bells and whistles because it operates on single cells.
1293 cell_tile_apply_pos (CellTile
**tile
, int level
,
1295 ReplacementStyle
*rs
)
1301 g_return_if_fail (col
>= 0);
1302 g_return_if_fail (col
< gnm_sheet_get_max_cols (rs
->sheet
));
1303 g_return_if_fail (row
>= 0);
1304 g_return_if_fail (row
< gnm_sheet_get_max_rows (rs
->sheet
));
1306 range_init (&rng
, col
, row
, col
, row
);
1309 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1310 g_return_if_fail (tile
!= NULL
);
1311 g_return_if_fail (*tile
!= NULL
);
1315 g_return_if_fail (TILE_SIMPLE
<= type
&& type
<= TILE_PTR_MATRIX
);
1318 int const w
= tile_widths
[level
];
1319 int const c
= col
/ w
;
1320 int const h
= tile_heights
[level
];
1321 int const r
= row
/ h
;
1323 if (type
!= TILE_PTR_MATRIX
) {
1324 /* applying the same style to part of a simple-tile is a nop */
1325 if (type
== TILE_SIMPLE
&&
1326 (*tile
)->style_simple
.style
[0] == rs
->new_style
)
1329 tmp
= cell_tile_ptr_matrix_new (tmp
);
1330 cell_tile_dtor (*tile
);
1333 tile
= tmp
->ptr_matrix
.ptr
+ r
* TILE_SIZE_COL
+ c
;
1337 goto tail_recursion
;
1338 } else if (type
!= TILE_MATRIX
)
1339 *tile
= tmp
= cell_tile_matrix_set (tmp
);
1341 g_return_if_fail (tmp
->type
== TILE_MATRIX
);
1342 rstyle_apply (tmp
->style_matrix
.style
+ row
* TILE_SIZE_COL
+ col
,
1348 * sheet_style_set_range:
1353 * Change the complete style for a region.
1354 * This function absorbs a reference to the new @style.
1357 sheet_style_set_range (Sheet
*sheet
, GnmRange
const *range
,
1360 ReplacementStyle rs
;
1363 g_return_if_fail (IS_SHEET (sheet
));
1364 g_return_if_fail (range
!= NULL
);
1366 if (range
->start
.col
> range
->end
.col
||
1367 range
->start
.row
> range
->end
.row
) {
1368 gnm_style_unref (style
);
1373 range_ensure_sanity (&r
, sheet
);
1375 rstyle_ctor_style (&rs
, style
, sheet
);
1376 cell_tile_apply (&sheet
->style_data
->styles
,
1377 sheet
->tile_top_level
, 0, 0,
1383 * sheet_style_apply_col:
1388 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1389 * will make life easier.
1391 * Apply a partial style to a full col.
1392 * The routine absorbs a reference to the partial style.
1395 sheet_style_apply_col (Sheet
*sheet
, int col
, GnmStyle
*pstyle
)
1398 range_init_cols (&r
, sheet
, col
, col
);
1399 sheet_style_apply_range (sheet
, &r
, pstyle
);
1403 * sheet_style_apply_row:
1408 * NOTE: This is a simple wrapper for now. When we support col/row styles it
1409 * will make life easier.
1411 * Apply a partial style to a full col.
1412 * The routine absorbs a reference to the partial style.
1415 sheet_style_apply_row (Sheet
*sheet
, int row
, GnmStyle
*pstyle
)
1418 range_init_rows (&r
, sheet
, row
, row
);
1419 sheet_style_apply_range (sheet
, &r
, pstyle
);
1423 * sheet_style_apply_pos:
1429 * Apply a partial style to a single cell
1430 * This function absorbs a reference to the new @style.
1433 sheet_style_apply_pos (Sheet
*sheet
, int col
, int row
,
1436 ReplacementStyle rs
;
1438 g_return_if_fail (IS_SHEET (sheet
));
1440 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1441 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1442 sheet
->tile_top_level
, col
, row
,
1447 * sheet_style_set_pos:
1453 * Change the complete style for a single cell.
1454 * This function absorbs a reference to the new @style.
1457 sheet_style_set_pos (Sheet
*sheet
, int col
, int row
,
1460 ReplacementStyle rs
;
1462 g_return_if_fail (IS_SHEET (sheet
));
1464 rstyle_ctor_style (&rs
, style
, sheet
);
1465 cell_tile_apply_pos (&sheet
->style_data
->styles
,
1466 sheet
->tile_top_level
, col
, row
,
1472 * sheet_style_default:
1475 * Returns a reference to default style for a sheet.
1478 sheet_style_default (Sheet
const *sheet
)
1480 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
1481 g_return_val_if_fail (sheet
->style_data
!= NULL
, NULL
);
1483 gnm_style_ref (sheet
->style_data
->default_style
);
1484 return sheet
->style_data
->default_style
;
1490 * @col: column number
1493 * Returns: (transfer none): find the fully qualified style applicable to
1494 * the specified cell position
1497 sheet_style_get (Sheet
const *sheet
, int col
, int row
)
1499 int level
= sheet
->tile_top_level
;
1500 CellTile
*tile
= sheet
->style_data
->styles
;
1503 int width
= tile_widths
[level
];
1504 int height
= tile_heights
[level
];
1505 int c
= col
/ width
;
1506 int r
= row
/ height
;
1508 g_return_val_if_fail (tile
!= NULL
, NULL
);
1509 g_return_val_if_fail (0 <= c
&& c
< TILE_SIZE_COL
, NULL
);
1510 g_return_val_if_fail (0 <= r
&& r
< TILE_SIZE_ROW
, NULL
);
1512 switch (tile
->type
) {
1514 return tile
->style_simple
.style
[0];
1516 return tile
->style_col
.style
[c
];
1518 return tile
->style_row
.style
[r
];
1520 return tile
->style_matrix
.style
[r
* TILE_SIZE_COL
+ c
];
1522 case TILE_PTR_MATRIX
:
1523 g_return_val_if_fail (level
> 0, NULL
);
1526 tile
= tile
->ptr_matrix
.ptr
[r
* TILE_SIZE_COL
+ c
];
1532 g_warning ("Adaptive Quad Tree corruption !");
1538 #define border_null(b) ((b) == none || (b) == NULL)
1541 style_row (GnmStyle
const *style
, int start_col
, int end_col
,
1542 GnmStyleRow
*sr
, gboolean accept_conditions
)
1544 GnmBorder
const *top
, *bottom
, *none
= gnm_style_border_none ();
1545 GnmBorder
const *left
, *right
, *v
;
1546 int const end
= MIN (end_col
, sr
->end_col
);
1547 int i
= MAX (start_col
, sr
->start_col
);
1548 GnmStyleConditions
*conds
;
1550 conds
= accept_conditions
1551 ? gnm_style_get_conditions (style
)
1557 for (eval_pos_init (&ep
, (Sheet
*)sr
->sheet
, i
, sr
->row
); ep
.eval
.col
<= end
; ep
.eval
.col
++) {
1558 res
= gnm_style_conditions_eval (conds
, &ep
);
1560 ? gnm_style_get_cond_style (style
, res
)
1562 ep
.eval
.col
, ep
.eval
.col
, sr
, FALSE
);
1567 top
= gnm_style_get_border (style
, MSTYLE_BORDER_TOP
);
1568 bottom
= gnm_style_get_border (style
, MSTYLE_BORDER_BOTTOM
);
1569 left
= gnm_style_get_border (style
, MSTYLE_BORDER_LEFT
);
1570 right
= gnm_style_get_border (style
, MSTYLE_BORDER_RIGHT
);
1572 /* Cancel grids if there is a background */
1573 if (sr
->hide_grid
|| gnm_style_get_pattern (style
) > 0) {
1584 if (left
!= none
&& border_null (sr
->vertical
[i
]))
1585 sr
->vertical
[i
] = left
;
1586 v
= border_null (right
) ? left
: right
;
1589 sr
->styles
[i
] = style
;
1590 if (top
!= none
&& border_null (sr
->top
[i
]))
1592 sr
->bottom
[i
] = bottom
;
1593 sr
->vertical
[++i
] = v
;
1595 if (border_null (right
))
1596 sr
->vertical
[i
] = right
;
1600 get_style_row (CellTile
const *tile
, int level
,
1601 int corner_col
, int corner_row
,
1604 int const width
= tile_widths
[level
+1];
1605 int const w
= tile_widths
[level
];
1606 int const h
= tile_heights
[level
];
1610 g_return_if_fail (TILE_TOP_LEVEL
>= level
&& level
>= 0);
1611 g_return_if_fail (tile
!= NULL
);
1615 if (t
!= TILE_SIMPLE
&& t
!= TILE_COL
) {
1616 r
= (sr
->row
> corner_row
) ? (sr
->row
- corner_row
)/ h
: 0;
1617 g_return_if_fail (r
< TILE_SIZE_ROW
);
1620 if (t
== TILE_ROW
|| t
== TILE_SIMPLE
) {
1621 style_row (tile
->style_any
.style
[r
],
1622 corner_col
, corner_col
+ width
- 1, sr
, TRUE
);
1624 /* find the start and end */
1626 int last_c
= (sr
->end_col
- corner_col
) / w
;
1627 if (last_c
>= TILE_SIZE_COL
)
1628 last_c
= TILE_SIZE_COL
-1;
1629 if (sr
->start_col
> corner_col
) {
1630 c
= (sr
->start_col
- corner_col
) / w
;
1631 corner_col
+= c
* w
;
1637 if (t
!= TILE_PTR_MATRIX
) {
1638 GnmStyle
* const *styles
= tile
->style_any
.style
+ r
*TILE_SIZE_COL
;
1640 for ( ; c
<= last_c
; c
++, corner_col
+= w
)
1641 style_row (styles
[c
],
1642 corner_col
, corner_col
+ w
- 1, sr
, TRUE
);
1644 CellTile
* const *tiles
= tile
->ptr_matrix
.ptr
+ r
*TILE_SIZE_COL
;
1646 g_return_if_fail (level
> 0);
1648 for ( level
-- ; c
<= last_c
; c
++, corner_col
+= w
)
1649 get_style_row (tiles
[c
], level
,
1650 corner_col
, corner_row
, sr
);
1656 * sheet_style_get_row:
1660 * A utility routine which efficiently retrieves a range of styles within a row.
1661 * It also merges adjacent borders as necessary.
1664 sheet_style_get_row (Sheet
const *sheet
, GnmStyleRow
*sr
)
1667 g_return_if_fail (IS_SHEET (sheet
));
1668 g_return_if_fail (sr
!= NULL
);
1669 g_return_if_fail (sr
->styles
!= NULL
);
1670 g_return_if_fail (sr
->vertical
!= NULL
);
1671 g_return_if_fail (sr
->top
!= NULL
);
1672 g_return_if_fail (sr
->bottom
!= NULL
);
1675 sr
->vertical
[sr
->start_col
] = gnm_style_border_none ();
1676 get_style_row (sheet
->style_data
->styles
, sheet
->tile_top_level
, 0, 0, sr
);
1680 cb_get_row (GnmStyle
*style
,
1681 int corner_col
, G_GNUC_UNUSED
int corner_row
,
1682 int width
, G_GNUC_UNUSED
int height
,
1683 GnmRange
const *apply_to
, gpointer user_
)
1685 GnmStyle
**res
= user_
;
1688 /* The given dimensions refer to the tile, not the area. */
1689 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
1691 for (i
= 0; i
< width
; i
++)
1692 res
[corner_col
+ i
] = style
;
1696 sheet_style_get_row2 (Sheet
const *sheet
, int row
)
1699 GnmStyle
**res
= g_new (GnmStyle
*, gnm_sheet_get_max_cols (sheet
));
1701 range_init_rows (&r
, sheet
, row
, row
);
1703 foreach_tile (sheet
, &r
, cb_get_row
, res
);
1712 * A small utility routine to initialize the grid drawing GnmStyleRow data
1716 style_row_init (GnmBorder
const * * *prev_vert
,
1717 GnmStyleRow
*sr
, GnmStyleRow
*next_sr
,
1718 int start_col
, int end_col
, gpointer mem
, gboolean hide_grid
)
1721 GnmBorder
const *none
= hide_grid
? NULL
: gnm_style_border_none ();
1723 /* alias the arrays for easy access so that array[col] is valid
1724 * for all elements start_col-1 .. end_col+1 inclusive.
1725 * Note that this means that in some cases array[-1] is legal.
1727 n
= end_col
- start_col
+ 3; /* 1 before, 1 after, 1 fencepost */
1729 sr
->vertical
-= start_col
-1;
1730 sr
->top
= sr
->vertical
+ n
;
1731 sr
->bottom
= sr
->top
+ n
;
1732 next_sr
->top
= sr
->bottom
; /* yes they should share */
1733 next_sr
->bottom
= next_sr
->top
+ n
;
1734 next_sr
->vertical
= next_sr
->bottom
+ n
;
1735 *prev_vert
= next_sr
->vertical
+ n
;
1736 sr
->styles
= ((GnmStyle
const **) (*prev_vert
+ n
));
1737 next_sr
->styles
= sr
->styles
+ n
;
1738 sr
->start_col
= next_sr
->start_col
= start_col
;
1739 sr
->end_col
= next_sr
->end_col
= end_col
;
1740 sr
->hide_grid
= next_sr
->hide_grid
= hide_grid
;
1742 /* Init the areas that sheet_style_get_row will not */
1743 for (col
= start_col
-1 ; col
<= end_col
+1; ++col
)
1744 (*prev_vert
)[col
] = sr
->top
[col
] = none
;
1745 sr
->vertical
[start_col
-1] = sr
->vertical
[end_col
+1] =
1746 next_sr
->vertical
[start_col
-1] = next_sr
->vertical
[end_col
+1] =
1747 next_sr
->top
[start_col
-1] = next_sr
->top
[end_col
+1] =
1748 next_sr
->bottom
[start_col
-1] = next_sr
->bottom
[end_col
+1] = none
;
1752 * sheet_style_apply_range: (skip)
1754 * @range: #GnmRange to apply over
1755 * @pstyle: (transfer full): A partial style to apply
1757 * Apply a partial style to a region.
1758 * The routine absorbs a reference to the partial style.
1761 sheet_style_apply_range (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1763 ReplacementStyle rs
;
1766 g_return_if_fail (IS_SHEET (sheet
));
1767 g_return_if_fail (range
!= NULL
);
1769 if (range
->start
.col
> range
->end
.col
||
1770 range
->start
.row
> range
->end
.row
) {
1771 gnm_style_unref (pstyle
);
1776 range_ensure_sanity (&r
, sheet
);
1778 rstyle_ctor_pstyle (&rs
, pstyle
, sheet
);
1779 cell_tile_apply (&sheet
->style_data
->styles
,
1780 sheet
->tile_top_level
, 0, 0,
1786 * sheet_style_apply_range2: (rename-to sheet_style_apply_range)
1788 * @range: #GnmRange to apply over
1789 * @pstyle: A partial style to apply
1791 * Apply a partial style to a region.
1794 sheet_style_apply_range2 (Sheet
*sheet
, GnmRange
const *range
, GnmStyle
*pstyle
)
1796 gnm_style_ref (pstyle
);
1797 sheet_style_apply_range (sheet
, range
, pstyle
);
1802 apply_border (Sheet
*sheet
, GnmRange
const *r
,
1803 GnmStyleBorderLocation side
,
1806 GnmStyle
*pstyle
= gnm_style_new ();
1807 pstyle_set_border (pstyle
, border
, side
);
1808 sheet_style_apply_range (sheet
, r
, pstyle
);
1812 * sheet_style_apply_border:
1817 * When a user applies a border to a region we attempt to remove the border
1818 * from the opposing side to avoid overlapping border specifications.
1820 * if we apply a top border to a range, we would clear the bottom border
1821 * of the range offset upwards.
1824 sheet_style_apply_border (Sheet
*sheet
,
1825 GnmRange
const *range
,
1826 GnmBorder
**borders
)
1828 GnmStyle
*pstyle
= NULL
;
1830 if (borders
== NULL
)
1833 if (borders
[GNM_STYLE_BORDER_TOP
]) {
1835 GnmRange r
= *range
;
1836 r
.end
.row
= r
.start
.row
;
1837 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1838 borders
[GNM_STYLE_BORDER_TOP
]);
1842 if (r
.start
.row
>= 0) {
1843 r
.end
.row
= r
.start
.row
;
1844 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1845 gnm_style_border_none ());
1849 if (borders
[GNM_STYLE_BORDER_BOTTOM
]) {
1850 /* 2.1 bottom inner */
1851 GnmRange r
= *range
;
1852 r
.start
.row
= r
.end
.row
;
1853 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1854 borders
[GNM_STYLE_BORDER_BOTTOM
]);
1856 /* 2.2 bottom outer */
1858 if (r
.end
.row
< gnm_sheet_get_last_row (sheet
)) {
1859 r
.start
.row
= r
.end
.row
;
1860 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1861 gnm_style_border_none ());
1865 if (borders
[GNM_STYLE_BORDER_LEFT
]) {
1866 /* 3.1 left inner */
1867 GnmRange r
= *range
;
1868 r
.end
.col
= r
.start
.col
;
1869 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1870 borders
[GNM_STYLE_BORDER_LEFT
]);
1872 /* 3.2 left outer */
1874 if (r
.start
.col
>= 0) {
1875 r
.end
.col
= r
.start
.col
;
1876 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1877 gnm_style_border_none ());
1881 if (borders
[GNM_STYLE_BORDER_RIGHT
]) {
1882 /* 4.1 right inner */
1883 GnmRange r
= *range
;
1884 r
.start
.col
= r
.end
.col
;
1885 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1886 borders
[GNM_STYLE_BORDER_RIGHT
]);
1888 /* 4.2 right outer */
1890 if (r
.end
.col
< gnm_sheet_get_last_col (sheet
)) {
1891 r
.start
.col
= r
.end
.col
;
1892 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1893 gnm_style_border_none ());
1897 /* Interiors horizontal : prefer top */
1898 if (borders
[GNM_STYLE_BORDER_HORIZ
] != NULL
) {
1899 /* 5.1 horizontal interior top */
1900 if (range
->start
.row
!= range
->end
.row
) {
1901 GnmRange r
= *range
;
1903 apply_border (sheet
, &r
, GNM_STYLE_BORDER_TOP
,
1904 borders
[GNM_STYLE_BORDER_HORIZ
]);
1906 /* 5.2 interior bottom */
1907 if (range
->start
.row
!= range
->end
.row
) {
1908 GnmRange r
= *range
;
1910 apply_border (sheet
, &r
, GNM_STYLE_BORDER_BOTTOM
,
1911 gnm_style_border_none ());
1915 /* Interiors vertical: prefer left */
1916 if (borders
[GNM_STYLE_BORDER_VERT
] != NULL
) {
1917 /* 6.1 vertical interior left */
1918 if (range
->start
.col
!= range
->end
.col
) {
1919 GnmRange r
= *range
;
1921 apply_border (sheet
, &r
, GNM_STYLE_BORDER_LEFT
,
1922 borders
[GNM_STYLE_BORDER_VERT
]);
1925 /* 6.2 The vertical interior right */
1926 if (range
->start
.col
!= range
->end
.col
) {
1927 GnmRange r
= *range
;
1929 apply_border (sheet
, &r
, GNM_STYLE_BORDER_RIGHT
,
1930 gnm_style_border_none ());
1934 /* 7. Diagonals (apply both in one pass) */
1935 if (borders
[GNM_STYLE_BORDER_DIAG
] != NULL
) {
1936 pstyle
= gnm_style_new ();
1937 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_DIAG
],
1938 GNM_STYLE_BORDER_DIAG
);
1940 if (borders
[GNM_STYLE_BORDER_REV_DIAG
]) {
1942 pstyle
= gnm_style_new ();
1943 pstyle_set_border (pstyle
, borders
[GNM_STYLE_BORDER_REV_DIAG
],
1944 GNM_STYLE_BORDER_REV_DIAG
);
1947 sheet_style_apply_range (sheet
, range
, pstyle
);
1950 /****************************************************************************/
1954 unsigned int conflicts
;
1958 cb_find_conflicts (GnmStyle
*style
,
1959 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
1960 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
1961 G_GNUC_UNUSED GnmRange
const *apply_to
, FindConflicts
*ptr
)
1963 ptr
->conflicts
= gnm_style_find_conflicts (ptr
->accum
, style
, ptr
->conflicts
);
1967 border_mask_internal (gboolean
*known
, GnmBorder
**borders
,
1968 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1972 borders
[l
] = (GnmBorder
*)b
;
1973 gnm_style_border_ref (borders
[l
]);
1974 } else if (borders
[l
] != b
&& borders
[l
] != NULL
) {
1975 gnm_style_border_unref (borders
[l
]);
1981 border_mask (gboolean
*known
, GnmBorder
**borders
,
1982 GnmBorder
const *b
, GnmStyleBorderLocation l
)
1985 b
= gnm_style_border_none ();
1986 border_mask_internal (known
, borders
, b
, l
);
1990 border_mask_vec (gboolean
*known
, GnmBorder
**borders
,
1991 GnmBorder
const * const *vec
, int first
, int last
,
1992 GnmStyleBorderLocation l
)
1994 GnmBorder
const *b
= vec
[first
];
1997 b
= gnm_style_border_none ();
1998 while (first
++ < last
) {
1999 GnmBorder
const *tmp
= vec
[first
];
2001 tmp
= gnm_style_border_none ();
2008 border_mask_internal (known
, borders
, b
, l
);
2012 * sheet_style_get_uniform:
2017 * Find out what style elements are common to every cell in a range
2018 * Returns a flag of TRUE if there was a conflict a given style element
2021 sheet_style_find_conflicts (Sheet
const *sheet
, GnmRange
const *r
,
2022 GnmStyle
**style
, GnmBorder
**borders
)
2024 int n
, col
, row
, start_col
, end_col
;
2026 gpointer
*sr_array_data
;
2027 GnmStyleBorderLocation i
;
2028 gboolean known
[GNM_STYLE_BORDER_EDGE_MAX
];
2029 GnmBorder
const *none
= gnm_style_border_none ();
2032 g_return_val_if_fail (IS_SHEET (sheet
), 0);
2033 g_return_val_if_fail (r
!= NULL
, 0);
2034 g_return_val_if_fail (style
!= NULL
, 0);
2035 g_return_val_if_fail (borders
!= NULL
, 0);
2037 /* init style set with a copy of the top left corner of the 1st range */
2038 if (*style
== NULL
) {
2039 GnmStyle
const *tmp
= sheet_style_get (sheet
, r
->start
.col
, r
->start
.row
);
2040 *style
= gnm_style_dup (tmp
);
2041 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++) {
2043 borders
[i
] = gnm_style_border_ref ((GnmBorder
*)none
);
2046 for (i
= GNM_STYLE_BORDER_TOP
; i
< GNM_STYLE_BORDER_EDGE_MAX
; i
++)
2050 user
.accum
= *style
;
2051 user
.conflicts
= 0; /* no conflicts yet */
2052 foreach_tile (sheet
, r
, (ForeachTileFunc
)cb_find_conflicts
, &user
);
2054 /* copy over the diagonals */
2055 for (i
= GNM_STYLE_BORDER_REV_DIAG
; i
<= GNM_STYLE_BORDER_DIAG
; i
++) {
2056 GnmStyleElement se
= GNM_STYLE_BORDER_LOCATION_TO_STYLE_ELEMENT (i
);
2057 if (user
.conflicts
& (1 << se
))
2060 borders
[i
] = gnm_style_border_ref (
2061 gnm_style_get_border (*style
, se
));
2064 start_col
= r
->start
.col
;
2065 if (r
->start
.col
> 0)
2067 end_col
= r
->end
.col
;
2068 if (r
->end
.col
< gnm_sheet_get_max_cols (sheet
))
2071 /* allocate then alias the arrays for easy access */
2072 n
= end_col
- start_col
+ 2;
2073 g_assert (sizeof (GnmBorder
*) == sizeof (gpointer
));
2074 g_assert (sizeof (GnmStyle
*) == sizeof (gpointer
));
2075 sr_array_data
= g_new (gpointer
, n
* 4);
2076 sr
.vertical
= (GnmBorder
const **)(sr_array_data
- start_col
);
2077 sr
.top
= (GnmBorder
const **)(sr_array_data
+ n
- start_col
);
2078 sr
.bottom
= (GnmBorder
const **)(sr_array_data
+ 2 * n
- start_col
);
2079 sr
.styles
= (GnmStyle
const **) (sr_array_data
+ 3 * n
- start_col
);
2080 sr
.start_col
= start_col
;
2081 sr
.end_col
= end_col
;
2082 sr
.hide_grid
= sheet
->hide_grid
;
2084 /* pretend the previous bottom had no borders */
2085 for (col
= start_col
; col
<= end_col
; ++col
)
2088 /* merge the bottom of the previous row */
2089 if (r
->start
.row
> 0) {
2090 GnmBorder
const ** roller
;
2091 sr
.row
= r
->start
.row
- 1;
2092 sheet_style_get_row (sheet
, &sr
);
2093 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2097 * TODO: The border handling is tricky and currently VERY slow for
2098 * large ranges. We could easily optimize this. There is no need to
2099 * retrieve the style in every cell just to do a filter for uniformity
2100 * by row. One day we should do a special case version of
2101 * sheet_style_get_row probably style_get_uniform_col (this will be
2104 for (row
= r
->start
.row
; row
<= r
->end
.row
; row
++) {
2105 GnmBorder
const **roller
;
2107 sheet_style_get_row (sheet
, &sr
);
2109 border_mask (known
, borders
, sr
.vertical
[r
->start
.col
],
2110 GNM_STYLE_BORDER_LEFT
);
2111 border_mask (known
, borders
, sr
.vertical
[r
->end
.col
+1],
2112 GNM_STYLE_BORDER_RIGHT
);
2113 border_mask_vec (known
, borders
, sr
.top
,
2114 r
->start
.col
, r
->end
.col
, (row
== r
->start
.row
)
2115 ? GNM_STYLE_BORDER_TOP
: GNM_STYLE_BORDER_HORIZ
);
2116 if (r
->start
.col
!= r
->end
.col
)
2117 border_mask_vec (known
, borders
, sr
.vertical
,
2118 r
->start
.col
+1, r
->end
.col
,
2119 GNM_STYLE_BORDER_VERT
);
2121 roller
= sr
.top
; sr
.top
= sr
.bottom
; sr
.bottom
= roller
;
2124 /* merge the top of the next row */
2125 if (r
->end
.row
< gnm_sheet_get_last_row (sheet
)) {
2126 sr
.row
= r
->end
.row
+ 1;
2127 sheet_style_get_row (sheet
, &sr
);
2129 border_mask_vec (known
, borders
, sr
.top
, r
->start
.col
, r
->end
.col
,
2130 GNM_STYLE_BORDER_BOTTOM
);
2132 g_free (sr_array_data
);
2133 return user
.conflicts
;
2137 * sheet_style_relocate:
2140 * Slide the styles from the origin region to the new position.
2143 sheet_style_relocate (GnmExprRelocateInfo
const *rinfo
)
2146 GnmStyleList
*styles
;
2148 g_return_if_fail (rinfo
!= NULL
);
2150 styles
= sheet_style_get_range (rinfo
->origin_sheet
, &rinfo
->origin
);
2152 sheet_style_set_range (rinfo
->origin_sheet
, &rinfo
->origin
,
2153 sheet_style_default (rinfo
->origin_sheet
));
2154 corner
.col
= rinfo
->origin
.start
.col
+ rinfo
->col_offset
;
2155 corner
.row
= rinfo
->origin
.start
.row
+ rinfo
->row_offset
;
2156 sheet_style_set_list (rinfo
->target_sheet
, &corner
, styles
, NULL
, NULL
);
2157 style_list_free (styles
);
2161 * sheet_style_insdel_colrow:
2164 * Insert of delete style columns/rows.
2166 * For the insert case, we stretch the preceding column/row into there space
2170 sheet_style_insdel_colrow (GnmExprRelocateInfo
const *rinfo
)
2172 GnmStyleList
*styles
= NULL
;
2177 g_return_if_fail (rinfo
!= NULL
);
2178 g_return_if_fail (rinfo
->origin_sheet
== rinfo
->target_sheet
);
2179 g_return_if_fail ((rinfo
->col_offset
== 0) != (rinfo
->row_offset
== 0));
2181 is_insert
= (rinfo
->col_offset
+ rinfo
->row_offset
> 0);
2182 sheet
= rinfo
->origin_sheet
;
2185 /* 1) copy col/row to the top/left of the region, and extend it */
2186 corner
= rinfo
->origin
.start
;
2187 if (rinfo
->col_offset
) {
2188 int col
= MAX (corner
.col
- 1, 0);
2193 range_init_cols (&r
, sheet
, col
, col
);
2194 styles
= sheet_style_get_range (sheet
, &r
);
2195 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2196 GnmStyleRegion
*sr
= ptr
->data
;
2197 sr
->range
.end
.col
= rinfo
->col_offset
- 1;
2200 int row
= MAX (corner
.row
- 1, 0);
2205 range_init_rows (&r
, sheet
, row
, row
);
2206 styles
= sheet_style_get_range (sheet
, &r
);
2207 for (ptr
= styles
; ptr
!= NULL
; ptr
= ptr
->next
) {
2208 GnmStyleRegion
*sr
= ptr
->data
;
2209 sr
->range
.end
.row
= rinfo
->row_offset
- 1;
2214 sheet_style_relocate (rinfo
);
2217 sheet_style_set_list (sheet
, &corner
, styles
, NULL
, NULL
);
2218 style_list_free (styles
);
2223 cb_style_extent (GnmStyle
*style
,
2224 int corner_col
, int corner_row
, int width
, int height
,
2225 GnmRange
const *apply_to
, gpointer user
)
2227 GnmRange
*res
= user
;
2228 if (gnm_style_visible_in_blank (style
)) {
2231 /* The given dimensions refer to the tile, not the area. */
2232 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2233 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2235 tmp
= corner_col
+width
-1;
2236 if (res
->end
.col
< tmp
)
2238 if (res
->start
.col
> corner_col
)
2239 res
->start
.col
= corner_col
;
2241 tmp
= corner_row
+height
-1;
2242 if (res
->end
.row
< tmp
)
2244 if (res
->start
.row
> corner_row
)
2245 res
->start
.row
= corner_row
;
2250 * sheet_style_get_extent:
2251 * @sheet: sheet to measure
2252 * @r: starting range and resulting range
2254 * A simple implementation that finds the smallest range containing all visible styles
2255 * and containing @res.
2258 sheet_style_get_extent (Sheet
const *sheet
, GnmRange
*res
)
2262 range_init_full_sheet (&r
, sheet
);
2263 foreach_tile (sheet
, &r
, cb_style_extent
, res
);
2266 struct cb_nondefault_extent
{
2268 GnmStyle
**col_defaults
;
2272 cb_nondefault_extent (GnmStyle
*style
,
2273 int corner_col
, int corner_row
, int width
, int height
,
2274 GnmRange
const *apply_to
, gpointer user_
)
2276 struct cb_nondefault_extent
*user
= user_
;
2277 GnmRange
*res
= user
->res
;
2280 for (i
= 0; i
< width
; i
++) {
2281 int col
= corner_col
+ i
;
2282 if (col
>= apply_to
->start
.col
&&
2283 col
<= apply_to
->end
.col
&&
2284 style
!= user
->col_defaults
[col
]) {
2285 int max_row
= MIN (corner_row
+ height
- 1,
2287 int min_row
= MAX (corner_row
, apply_to
->start
.row
);
2289 res
->start
.col
= MIN (col
, res
->start
.col
);
2290 res
->start
.row
= MIN (min_row
, res
->start
.row
);
2292 res
->end
.col
= MAX (col
, res
->end
.col
);
2293 res
->end
.row
= MAX (max_row
, res
->end
.row
);
2299 sheet_style_get_nondefault_extent (Sheet
const *sheet
, GnmRange
*extent
,
2300 const GnmRange
*src
, GnmStyle
**col_defaults
)
2302 struct cb_nondefault_extent user
;
2304 user
.col_defaults
= col_defaults
;
2305 foreach_tile (sheet
, src
, cb_nondefault_extent
, &user
);
2308 struct cb_is_default
{
2310 GnmStyle
**col_defaults
;
2314 cb_is_default (GnmStyle
*style
,
2315 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2316 int width
, G_GNUC_UNUSED
int height
,
2317 GnmRange
const *apply_to
, gpointer user_
)
2319 struct cb_is_default
*user
= user_
;
2322 /* The given "width" refers to the tile, not the area. */
2323 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2325 for (i
= 0; user
->res
&& i
< width
; i
++) {
2326 if (style
!= user
->col_defaults
[corner_col
+ i
])
2332 sheet_style_is_default (Sheet
const *sheet
, const GnmRange
*r
, GnmStyle
**col_defaults
)
2334 struct cb_is_default user
;
2337 user
.col_defaults
= col_defaults
;
2339 foreach_tile (sheet
, r
, cb_is_default
, &user
);
2344 struct cb_get_nondefault
{
2346 GnmStyle
**col_defaults
;
2350 cb_get_nondefault (GnmStyle
*style
,
2351 int corner_col
, G_GNUC_UNUSED
int corner_row
,
2352 int width
, G_GNUC_UNUSED
int height
,
2353 GnmRange
const *apply_to
, gpointer user_
)
2355 struct cb_get_nondefault
*user
= user_
;
2358 /* The given dimensions refer to the tile, not the area. */
2359 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2360 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2362 for (i
= 0; i
< width
; i
++) {
2363 if (style
!= user
->col_defaults
[corner_col
+ i
]) {
2365 for (j
= 0; j
< height
; j
++)
2366 user
->res
[corner_row
+ j
] = 1;
2373 sheet_style_get_nondefault_rows (Sheet
const *sheet
, GnmStyle
**col_defaults
)
2375 struct cb_get_nondefault user
;
2378 range_init_full_sheet (&r
, sheet
);
2380 user
.res
= g_new0 (guint8
, gnm_sheet_get_max_rows (sheet
));
2381 user
.col_defaults
= col_defaults
;
2383 foreach_tile (sheet
, &r
, cb_get_nondefault
, &user
);
2388 struct cb_most_common
{
2395 cb_most_common (GnmStyle
*style
,
2396 int corner_col
, int corner_row
, int width
, int height
,
2397 GnmRange
const *apply_to
, gpointer user
)
2399 struct cb_most_common
*cmc
= user
;
2400 int *counts
= g_hash_table_lookup (cmc
->h
, style
);
2403 counts
= g_new0 (int, cmc
->l
);
2404 g_hash_table_insert (cmc
->h
, style
, counts
);
2407 /* The given dimensions refer to the tile, not the area. */
2408 width
= MIN (width
, apply_to
->end
.col
- corner_col
+ 1);
2409 height
= MIN (height
, apply_to
->end
.row
- corner_row
+ 1);
2412 for (i
= 0; i
< width
; i
++)
2413 counts
[corner_col
+ i
] += height
;
2415 for (i
= 0; i
< height
; i
++)
2416 counts
[corner_row
+ i
] += width
;
2420 * sheet_style_most_common:
2421 * @sheet: sheet to inspect
2422 * @is_col: if %TRUE, look for common styles in columns; if FALSE, look in rows.
2424 * Returns: an array of styles describing the most common styles, one per column
2428 sheet_style_most_common (Sheet
const *sheet
, gboolean is_col
)
2431 struct cb_most_common cmc
;
2434 GHashTableIter iter
;
2435 gpointer key
, value
;
2437 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2439 range_init_full_sheet (&r
, sheet
);
2440 cmc
.h
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, NULL
, g_free
);
2441 cmc
.l
= colrow_max (is_col
, sheet
);
2442 cmc
.is_col
= is_col
;
2443 foreach_tile (sheet
, &r
, cb_most_common
, &cmc
);
2445 max
= g_new0 (int, cmc
.l
);
2446 res
= g_new0 (GnmStyle
*, cmc
.l
);
2447 g_hash_table_iter_init (&iter
, cmc
.h
);
2448 while (g_hash_table_iter_next (&iter
, &key
, &value
)) {
2449 int *counts
= value
;
2450 GnmStyle
*style
= key
;
2452 for (j
= 0; j
< cmc
.l
; j
++) {
2453 /* FIXME: we really ought to break ties in a
2454 consistent way that does not depend on hash
2456 if (counts
[j
] > max
[j
]) {
2462 g_hash_table_destroy (cmc
.h
);
2468 /****************************************************************************/
2471 * gnm_style_region_new:
2475 * Returns: (transfer full): the newly allocated #GnmStyleRegion.
2478 gnm_style_region_new (GnmRange
const *range
, GnmStyle
*style
)
2482 sr
= g_new (GnmStyleRegion
, 1);
2485 gnm_style_ref (style
);
2491 gnm_style_region_free (GnmStyleRegion
*sr
)
2493 g_return_if_fail (sr
!= NULL
);
2495 gnm_style_unref (sr
->style
);
2500 static GnmStyleRegion
*
2501 gnm_style_region_copy (GnmStyleRegion
*sr
)
2503 GnmStyleRegion
*res
= g_new (GnmStyleRegion
, 1);
2505 gnm_style_ref (sr
->style
);
2510 gnm_style_region_get_type (void)
2515 t
= g_boxed_type_register_static ("GnmStyleRegion",
2516 (GBoxedCopyFunc
)gnm_style_region_copy
,
2517 (GBoxedFreeFunc
)gnm_style_region_free
);
2524 debug_style_list (void)
2526 static int debug
= -1;
2528 debug
= gnm_debug_flag ("style-list");
2534 GHashTable
*by_tl
, *by_br
;
2536 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
);
2537 gboolean (*style_filter
) (GnmStyle
const *style
);
2538 GnmSheetSize
const *sheet_size
;
2542 merge_ranges (GnmRange
*a
, GnmRange
const *b
)
2544 if (a
->start
.row
== b
->start
.row
&&
2545 a
->end
.row
== b
->end
.row
&&
2546 a
->end
.col
+ 1 == b
->start
.col
) {
2547 /* "a" is just left of "b". */
2548 a
->end
.col
= b
->end
.col
;
2552 if (a
->start
.col
== b
->start
.col
&&
2553 a
->end
.col
== b
->end
.col
&&
2554 a
->end
.row
+ 1 == b
->start
.row
) {
2555 /* "a" is just on top of "b". */
2556 a
->end
.row
= b
->end
.row
;
2565 try_merge_pair (ISL
*data
, unsigned ui1
, unsigned ui2
)
2570 if (ui1
>= data
->accum
->len
|| ui2
>= data
->accum
->len
)
2573 a
= g_ptr_array_index (data
->accum
, ui1
);
2574 b
= g_ptr_array_index (data
->accum
, ui2
);
2576 if (!data
->style_equal (a
->style
, b
->style
))
2579 if (!merge_ranges (&a
->range
, &b
->range
))
2582 gnm_style_region_free (b
);
2583 g_ptr_array_remove_index (data
->accum
, ui2
);
2589 cb_style_list_add_node (GnmStyle
*style
,
2590 int corner_col
, int corner_row
, int width
, int height
,
2591 GnmRange
const *apply_to
, gpointer user_
)
2594 GnmSheetSize
const *ss
= data
->sheet_size
;
2598 /* Can this even happen? */
2599 if (corner_col
>= ss
->max_cols
|| corner_row
>= ss
->max_rows
)
2602 if (data
->style_filter
&& !data
->style_filter (style
))
2605 range
.start
.col
= corner_col
;
2606 range
.start
.row
= corner_row
;
2607 range
.end
.col
= MIN (corner_col
+ width
- 1, ss
->max_cols
- 1);
2608 range
.end
.row
= MIN (corner_row
+ height
- 1, ss
->max_rows
- 1);
2611 range
.start
.col
-= apply_to
->start
.col
;
2612 if (range
.start
.col
< 0)
2613 range
.start
.col
= 0;
2614 range
.start
.row
-= apply_to
->start
.row
;
2615 if (range
.start
.row
< 0)
2616 range
.start
.row
= 0;
2618 if (range
.end
.col
> apply_to
->end
.col
)
2619 range
.end
.col
= apply_to
->end
.col
;
2620 range
.end
.col
-= apply_to
->start
.col
;
2621 if (range
.end
.row
> apply_to
->end
.row
)
2622 range
.end
.row
= apply_to
->end
.row
;
2623 range
.end
.row
-= apply_to
->start
.row
;
2626 data
->area
+= (guint64
)range_width (&range
) * range_height (&range
);
2628 sr
= gnm_style_region_new (&range
, style
);
2629 g_ptr_array_add (data
->accum
, sr
);
2631 while (try_merge_pair (data
, data
->accum
->len
- 2, data
->accum
->len
- 1))
2636 verify_hashes (ISL
*data
)
2638 GHashTable
*by_tl
= data
->by_tl
;
2639 GHashTable
*by_br
= data
->by_br
;
2643 g_return_if_fail (g_hash_table_size (by_tl
) == data
->accum
->len
);
2644 g_return_if_fail (g_hash_table_size (by_br
) == data
->accum
->len
);
2646 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2647 GnmStyleRegion
*sr
= g_ptr_array_index (data
->accum
, ui
);
2648 g_return_if_fail (g_hash_table_lookup (by_tl
, &sr
->range
.start
) == sr
);
2649 g_return_if_fail (g_hash_table_lookup (by_br
, &sr
->range
.end
) == sr
);
2650 area
+= range_height (&sr
->range
) *
2651 (guint64
)range_width (&sr
->range
);
2654 g_return_if_fail (area
== data
->area
);
2658 merge_vertical_stripes (ISL
*data
)
2661 GHashTable
*by_tl
= data
->by_tl
;
2662 GHashTable
*by_br
= data
->by_br
;
2663 gboolean debug
= debug_style_list ();
2664 gboolean paranoid
= debug
;
2666 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2667 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2670 GSList
*Bs
= NULL
, *l
;
2671 gboolean fail
= FALSE
;
2673 /* We're looking for the setup below and extend Bs down */
2674 /* taking over part of C which is then extended to */
2675 /* include all of A. */
2679 /* +---------+ B1 | B2 | */
2681 /* +---------+----+---------+ */
2683 /* +------------------------+ */
2685 cr
.col
= a
->range
.start
.col
;
2686 cr
.row
= a
->range
.end
.row
+ 1;
2687 c
= g_hash_table_lookup (by_tl
, &cr
);
2688 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2691 cr
.col
= c
->range
.end
.col
;
2692 cr
.row
= a
->range
.end
.row
;
2693 while (cr
.col
> a
->range
.end
.col
) {
2694 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2695 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2699 Bs
= g_slist_prepend (Bs
, b
);
2700 cr
.col
= b
->range
.start
.col
- 1;
2702 if (fail
|| cr
.col
!= a
->range
.end
.col
) {
2708 g_printerr ("Vertical stripe merge:\n");
2709 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2710 for (l
= Bs
; l
; l
= l
-> next
) {
2711 GnmStyleRegion
*b
= l
->data
;
2712 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2714 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2717 g_hash_table_remove (by_tl
, &a
->range
.start
);
2718 g_hash_table_remove (by_br
, &a
->range
.end
);
2719 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2722 g_hash_table_remove (by_tl
, &c
->range
.start
);
2723 g_hash_table_remove (by_br
, &c
->range
.end
);
2724 c
->range
.start
.row
= a
->range
.start
.row
;
2725 c
->range
.end
.col
= a
->range
.end
.col
;
2726 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2727 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2729 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2731 for (l
= Bs
; l
; l
= l
-> next
) {
2732 GnmStyleRegion
*b
= l
->data
;
2733 g_hash_table_remove (by_br
, &b
->range
.end
);
2734 b
->range
.end
.row
= c
->range
.end
.row
;
2735 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2737 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2742 gnm_style_region_free (a
);
2745 if (paranoid
) verify_hashes (data
);
2750 merge_horizontal_stripes (ISL
*data
)
2753 GHashTable
*by_tl
= data
->by_tl
;
2754 GHashTable
*by_br
= data
->by_br
;
2755 gboolean debug
= debug_style_list ();
2756 gboolean paranoid
= debug
;
2758 for (ui
= 0; ui
< data
->accum
->len
; ui
++) {
2759 GnmStyleRegion
*a
= g_ptr_array_index (data
->accum
, ui
);
2762 GSList
*Bs
= NULL
, *l
;
2763 gboolean fail
= FALSE
;
2765 /* We're looking for the setup below and extend Bs right */
2766 /* taking over part of C which is then extended to */
2767 /* include all of A. */
2771 /* +----+-----+ | */
2773 /* +--+-------+ | */
2779 /* +-------+-----+ */
2781 cr
.col
= a
->range
.end
.col
+ 1;
2782 cr
.row
= a
->range
.start
.row
;
2783 c
= g_hash_table_lookup (by_tl
, &cr
);
2784 if (!c
|| !data
->style_equal (a
->style
, c
->style
))
2787 cr
.col
= a
->range
.end
.col
;
2788 cr
.row
= c
->range
.end
.row
;
2789 while (cr
.row
> a
->range
.end
.row
) {
2790 GnmStyleRegion
*b
= g_hash_table_lookup (by_br
, &cr
);
2791 if (!b
|| !data
->style_equal (a
->style
, b
->style
)) {
2795 Bs
= g_slist_prepend (Bs
, b
);
2796 cr
.row
= b
->range
.start
.row
- 1;
2798 if (fail
|| cr
.row
!= a
->range
.end
.row
) {
2804 g_printerr ("Horizontal stripe merge:\n");
2805 g_printerr ("A: %s\n", range_as_string (&a
->range
));
2806 for (l
= Bs
; l
; l
= l
-> next
) {
2807 GnmStyleRegion
*b
= l
->data
;
2808 g_printerr ("B: %s\n", range_as_string (&b
->range
));
2810 g_printerr ("C: %s\n", range_as_string (&c
->range
));
2813 g_hash_table_remove (by_tl
, &a
->range
.start
);
2814 g_hash_table_remove (by_br
, &a
->range
.end
);
2815 g_ptr_array_remove_index_fast (data
->accum
, ui
);
2818 g_hash_table_remove (by_tl
, &c
->range
.start
);
2819 g_hash_table_remove (by_br
, &c
->range
.end
);
2820 c
->range
.start
.col
= a
->range
.start
.col
;
2821 c
->range
.end
.row
= a
->range
.end
.row
;
2822 g_hash_table_insert (by_tl
, &c
->range
.start
, c
);
2823 g_hash_table_insert (by_br
, &c
->range
.end
, c
);
2825 g_printerr ("New C: %s\n", range_as_string (&c
->range
));
2827 for (l
= Bs
; l
; l
= l
-> next
) {
2828 GnmStyleRegion
*b
= l
->data
;
2829 g_hash_table_remove (by_br
, &b
->range
.end
);
2830 b
->range
.end
.col
= c
->range
.end
.col
;
2831 g_hash_table_insert (by_br
, &b
->range
.end
, b
);
2833 g_printerr ("New B: %s\n", range_as_string (&b
->range
));
2838 gnm_style_region_free (a
);
2841 if (paranoid
) verify_hashes (data
);
2846 by_col_row (GnmStyleRegion
**a
, GnmStyleRegion
**b
)
2850 d
= (*a
)->range
.start
.col
- (*b
)->range
.start
.col
;
2854 d
= (*a
)->range
.start
.row
- (*b
)->range
.start
.row
;
2858 static GnmStyleList
*
2859 internal_style_list (Sheet
const *sheet
, GnmRange
const *r
,
2860 gboolean (*style_equal
) (GnmStyle
const *a
, GnmStyle
const *b
),
2861 gboolean (*style_filter
) (GnmStyle
const *style
))
2863 GnmRange full_sheet
;
2865 GnmStyleList
*res
= NULL
;
2866 unsigned ui
, prelen
;
2867 gboolean paranoid
= FALSE
;
2870 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
2873 /* This can happen if the last row or column is deleted. */
2874 if (!range_valid (r
))
2877 r
= range_init_full_sheet (&full_sheet
, sheet
);
2879 data
.accum
= g_ptr_array_new ();
2880 data
.by_tl
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2881 (GEqualFunc
)gnm_cellpos_equal
);
2882 data
.by_br
= g_hash_table_new ((GHashFunc
)gnm_cellpos_hash
,
2883 (GEqualFunc
)gnm_cellpos_equal
);
2885 data
.style_equal
= style_equal
;
2886 data
.style_filter
= style_filter
;
2887 data
.sheet_size
= gnm_sheet_get_size (sheet
);
2889 foreach_tile (sheet
, r
, cb_style_list_add_node
, &data
);
2891 sheet_area
= (guint64
)range_height (r
) * range_width (r
);
2892 if (data
.style_filter
? (data
.area
> sheet_area
) : (data
.area
!= sheet_area
))
2893 g_warning ("Strange size issue in internal_style_list");
2896 * Simple, fast optimization first. For the file underlying
2897 * bug 699045 this brings down 332688 entries to just 86.
2899 if (data
.accum
->len
>= 2) {
2900 g_ptr_array_sort (data
.accum
, (GCompareFunc
)by_col_row
);
2901 for (ui
= data
.accum
->len
- 1; ui
> 0; ui
--) {
2902 try_merge_pair (&data
, ui
- 1, ui
);
2906 /* Populate hashes. */
2907 for (ui
= 0; ui
< data
.accum
->len
; ui
++) {
2908 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2909 g_hash_table_insert (data
.by_tl
, &sr
->range
.start
, sr
);
2910 g_hash_table_insert (data
.by_br
, &sr
->range
.end
, sr
);
2913 if (paranoid
) verify_hashes (&data
);
2916 prelen
= data
.accum
->len
;
2917 merge_vertical_stripes (&data
);
2918 merge_horizontal_stripes (&data
);
2919 } while (prelen
> data
.accum
->len
);
2921 /* Always verify once. */
2922 verify_hashes (&data
);
2924 if (debug_style_list ())
2925 g_printerr ("Total of %d ranges:\n", data
.accum
->len
);
2926 for (ui
= data
.accum
->len
; ui
-- > 0; ) {
2927 GnmStyleRegion
*sr
= g_ptr_array_index (data
.accum
, ui
);
2928 if (debug_style_list ())
2929 g_printerr (" %s %p\n",
2930 range_as_string (&sr
->range
),
2932 res
= g_slist_prepend (res
, sr
);
2935 g_ptr_array_free (data
.accum
, TRUE
);
2936 g_hash_table_destroy (data
.by_tl
);
2937 g_hash_table_destroy (data
.by_br
);
2942 * sheet_style_get_range:
2943 * @sheet: the sheet in which to find styles
2944 * @r: optional range to scan
2946 * Get a list of rectangles and their associated styles.
2947 * Caller is responsible for freeing. Note that when a range is given,
2948 * the resulting ranges are relative to the input range.
2950 * Returns: (transfer full):
2953 sheet_style_get_range (Sheet
const *sheet
, GnmRange
const *r
)
2955 return internal_style_list (sheet
, r
,
2961 style_conditions_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2963 return gnm_style_get_conditions (a
) == gnm_style_get_conditions (b
);
2967 style_conditions_filter (GnmStyle
const *style
)
2969 return gnm_style_get_conditions (style
) != NULL
;
2973 * sheet_style_collect_conditions:
2977 * Returns: (transfer full): a list of areas with conditionals, Caller is
2978 * responsible for freeing.
2981 sheet_style_collect_conditions (Sheet
const *sheet
, GnmRange
const *r
)
2983 return internal_style_list (sheet
, r
,
2984 style_conditions_equal
,
2985 style_conditions_filter
);
2990 style_hlink_equal (GnmStyle
const *a
, GnmStyle
const *b
)
2992 return gnm_style_get_hlink (a
) == gnm_style_get_hlink (b
);
2996 style_hlink_filter (GnmStyle
const *style
)
2998 return gnm_style_get_hlink (style
) != NULL
;
3002 * sheet_style_collect_hlinks:
3006 * Returns: (transfer full): a list of areas with hyperlinks, Caller is
3007 * responsible for freeing.
3010 sheet_style_collect_hlinks (Sheet
const *sheet
, GnmRange
const *r
)
3012 return internal_style_list (sheet
, r
,
3014 style_hlink_filter
);
3019 style_validation_equal (GnmStyle
const *a
, GnmStyle
const *b
)
3021 return gnm_style_get_validation (a
) == gnm_style_get_validation (b
) &&
3022 gnm_style_get_input_msg (a
) == gnm_style_get_input_msg (b
);
3026 style_validation_filter (GnmStyle
const *style
)
3028 return (gnm_style_get_validation (style
) != NULL
||
3029 gnm_style_get_input_msg (style
) != NULL
);
3033 * sheet_style_collect_validations:
3034 * @sheet: the to trawl
3035 * @r: (allow-none): range to restrict to
3037 * Returns: (transfer full): a list of areas with validation or input
3041 sheet_style_collect_validations (Sheet
const *sheet
, GnmRange
const *r
)
3043 return internal_style_list (sheet
, r
,
3044 style_validation_equal
,
3045 style_validation_filter
);
3049 * sheet_style_set_list:
3051 * @corner: The top-left corner (in LTR mode)
3053 * @range_modify: (scope call):
3056 * Overwrites the styles of the ranges given by @corner with the content of
3057 * @list. Optionally transposing the ranges
3060 sheet_style_set_list (Sheet
*sheet
, GnmCellPos
const *corner
,
3061 GnmStyleList
const *list
,
3062 sheet_style_set_list_cb_t range_modify
,
3065 GnmSpanCalcFlags spanflags
= GNM_SPANCALC_SIMPLE
;
3066 GnmStyleList
const *l
;
3068 g_return_val_if_fail (IS_SHEET (sheet
), spanflags
);
3070 /* Sluggish but simple implementation for now */
3071 for (l
= list
; l
; l
= l
->next
) {
3072 GnmStyleRegion
const *sr
= l
->data
;
3073 GnmRange r
= sr
->range
;
3075 range_translate (&r
, sheet
, +corner
->col
, +corner
->row
);
3077 range_modify (&r
, sheet
, data
);
3079 gnm_style_ref (sr
->style
);
3080 sheet_style_set_range (sheet
, &r
, sr
->style
);
3081 spanflags
|= gnm_style_required_spanflags (sr
->style
);
3088 * @l: the list to free
3090 * Free up the ressources in the style list. Including unreferencing the
3094 style_list_free (GnmStyleList
*list
)
3096 g_slist_free_full (list
, (GDestroyNotify
)gnm_style_region_free
);
3100 * style_list_get_style:
3105 * Attempts to find the style associated with the @pos offset within the 0,0
3107 * The resulting style does not have its reference count bumped.
3110 style_list_get_style (GnmStyleList
const *list
, int col
, int row
)
3112 GnmStyleList
const *l
;
3114 for (l
= list
; l
; l
= l
->next
) {
3115 GnmStyleRegion
const *sr
= l
->data
;
3116 GnmRange
const *r
= &sr
->range
;
3117 if (range_contains (r
, col
, row
))
3124 cb_find_link (GnmStyle
*style
,
3125 G_GNUC_UNUSED
int corner_col
, G_GNUC_UNUSED
int corner_row
,
3126 G_GNUC_UNUSED
int width
, G_GNUC_UNUSED
int height
,
3127 G_GNUC_UNUSED GnmRange
const *apply_to
, gpointer user
)
3129 GnmHLink
**plink
= user
;
3131 *plink
= gnm_style_get_hlink (style
);
3135 * sheet_style_region_contains_link:
3139 * Utility routine that checks to see if a region contains at least 1 hyper link
3140 * and returns the 1st one it finds.
3142 * Returns: (transfer none): the found #GmHLink if any.
3145 sheet_style_region_contains_link (Sheet
const *sheet
, GnmRange
const *r
)
3147 GnmHLink
*res
= NULL
;
3149 g_return_val_if_fail (IS_SHEET (sheet
), NULL
);
3150 g_return_val_if_fail (r
!= NULL
, NULL
);
3152 foreach_tile (sheet
, r
, cb_find_link
, &res
);
3157 * sheet_style_foreach:
3159 * @func: (scope call): callback
3160 * @user_data: user data.
3162 * Executes @func for each style in the sheet.
3165 sheet_style_foreach (Sheet
const *sheet
, GFunc func
, gpointer user_data
)
3169 g_return_if_fail (IS_SHEET (sheet
));
3170 g_return_if_fail (sheet
->style_data
!= NULL
);
3172 styles
= sh_all_styles (sheet
->style_data
->style_hash
);
3173 styles
= g_slist_sort (styles
, (GCompareFunc
)gnm_style_cmp
);
3174 g_slist_foreach (styles
, func
, user_data
);
3175 g_slist_free (styles
);
3179 * sheet_style_range_foreach:
3181 * @r: optional range
3182 * @func: (scope call): callback.
3183 * @user_data: user data
3187 sheet_style_range_foreach (Sheet
const *sheet
, GnmRange
const *r
,
3188 GHFunc func
, gpointer user_data
)
3190 GnmStyleList
*styles
, *l
;
3192 styles
= sheet_style_get_range (sheet
, r
);
3194 for (l
= styles
; l
; l
= l
->next
) {
3195 GnmStyleRegion
*sr
= l
->data
;
3197 sr
->range
.start
.col
+= r
->start
.col
;
3198 sr
->range
.start
.row
+= r
->start
.row
;
3199 sr
->range
.end
.col
+= r
->start
.col
;
3200 sr
->range
.end
.row
+= r
->start
.row
;
3202 func (NULL
, sr
, user_data
);
3203 gnm_style_region_free (sr
);
3206 g_slist_free (styles
);
3209 /* ------------------------------------------------------------------------- */
3212 cell_tile_dump (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3216 int const w
= tile_widths
[level
];
3217 int const h
= tile_heights
[level
];
3219 const char *indent
= "";
3221 type
= (*tile
)->type
;
3225 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3226 data
->ss
->max_cols
- 1),
3227 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3228 data
->ss
->max_rows
- 1));
3230 g_printerr ("%s%s: type %s\n",
3232 range_as_string (&rng
),
3233 tile_type_str
[type
]);
3235 if (type
== TILE_PTR_MATRIX
) {
3238 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3239 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3240 int c
= i
% TILE_SIZE_COL
;
3241 int r
= i
/ TILE_SIZE_COL
;
3242 cell_tile_dump (subtile
, level
- 1, data
,
3250 for (i
= 0; i
< tile_size
[type
]; i
++) {
3251 g_printerr ("%s: %d %p\n",
3254 (*tile
)->style_any
.style
[i
]);
3260 /* ------------------------------------------------------------------------- */
3263 cell_tile_optimize (CellTile
**tile
, int level
, CellTileOptimize
*data
,
3267 int const w
= tile_widths
[level
];
3268 int const h
= tile_heights
[level
];
3273 type
= (*tile
)->type
;
3274 if (type
== TILE_SIMPLE
)
3279 MIN (ccol
+ tile_widths
[level
+ 1] - 1,
3280 data
->ss
->max_cols
- 1),
3281 MIN (crow
+ tile_heights
[level
+ 1] - 1,
3282 data
->ss
->max_rows
- 1));
3287 if (!tile_is_uniform (*tile
))
3293 case TILE_PTR_MATRIX
: {
3294 gboolean all_simple
= TRUE
;
3297 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3298 CellTile
**subtile
= (*tile
)->ptr_matrix
.ptr
+ i
;
3299 if (data
->recursion
) {
3300 int c
= i
% TILE_SIZE_COL
;
3301 int r
= i
/ TILE_SIZE_COL
;
3302 cell_tile_optimize (subtile
, level
- 1, data
,
3306 if ((*subtile
)->type
!= TILE_SIMPLE
)
3312 res
= cell_tile_style_new (NULL
, TILE_MATRIX
);
3313 for (i
= 0; i
< TILE_SIZE_COL
* TILE_SIZE_ROW
; i
++) {
3314 CellTile
*subtile
= (*tile
)->ptr_matrix
.ptr
[i
];
3315 GnmStyle
*st
= subtile
->style_simple
.style
[0];
3316 gnm_style_link (st
);
3317 res
->style_matrix
.style
[i
] = st
;
3320 if (debug_style_optimize
)
3321 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3322 range_as_string (&rng
),
3323 range_width (&rng
), range_height (&rng
),
3324 tile_type_str
[(*tile
)->type
],
3325 tile_type_str
[res
->type
]);
3326 cell_tile_dtor (*tile
);
3333 gboolean csame
= TRUE
;
3334 gboolean rsame
= TRUE
;
3337 for (i
= r
= 0 ; r
< TILE_SIZE_ROW
; ++r
, i
+= TILE_SIZE_COL
) {
3338 for (c
= 0 ; c
< TILE_SIZE_COL
; ++c
) {
3340 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3341 (*tile
)->style_matrix
.style
[i
])) {
3347 !gnm_style_eq ((*tile
)->style_matrix
.style
[i
+ c
],
3348 (*tile
)->style_matrix
.style
[ c
])) {
3367 g_assert_not_reached ();
3370 if (debug_style_optimize
)
3371 g_printerr ("Turning %s (%dx%d) from a %s into a %s\n",
3372 range_as_string (&rng
),
3373 range_width (&rng
), range_height (&rng
),
3374 tile_type_str
[(*tile
)->type
],
3375 tile_type_str
[type
]);
3377 res
= cell_tile_style_new (NULL
, type
);
3380 res
->style_simple
.style
[0] = (*tile
)->style_any
.style
[0];
3383 for (i
= 0; i
< TILE_SIZE_ROW
; i
++)
3384 res
->style_row
.style
[i
] =
3385 (*tile
)->style_matrix
.style
[i
* TILE_SIZE_COL
];
3388 for (i
= 0; i
< TILE_SIZE_COL
; i
++)
3389 res
->style_col
.style
[i
] =
3390 (*tile
)->style_matrix
.style
[i
];
3393 g_assert_not_reached ();
3396 for (i
= 0; i
< tile_size
[type
]; i
++)
3397 gnm_style_link (res
->style_any
.style
[i
]);
3399 cell_tile_dtor (*tile
);
3404 sample_styles (Sheet
*sheet
)
3406 GnmSheetSize
const *ss
= gnm_sheet_get_size (sheet
);
3412 GnmStyle
const *mstyle
= sheet_style_get (sheet
, c
, r
);
3413 if (res
== NULL
|| !gnm_style_eq (mstyle
, res
->data
)) {
3414 gnm_style_ref (mstyle
);
3415 res
= g_slist_prepend (res
, GINT_TO_POINTER (c
));
3416 res
= g_slist_prepend (res
, GINT_TO_POINTER (r
));
3417 res
= g_slist_prepend (res
, (gpointer
)mstyle
);
3421 if (c
>= ss
->max_cols
) {
3424 if (r
>= ss
->max_rows
)
3429 return g_slist_reverse (res
);
3433 verify_styles (GSList
*pre
, GSList
*post
)
3435 GSList
*lpre
, *lpost
;
3436 gboolean silent
= FALSE
, bad
= FALSE
;
3438 for (lpre
= pre
, lpost
= post
;
3440 lpre
= (lpre
? lpre
->next
->next
->next
: NULL
),
3441 lpost
= (lpost
? lpost
->next
->next
->next
: NULL
)) {
3442 int cpre
= lpre
? GPOINTER_TO_INT (lpre
->data
) : -1;
3443 int rpre
= lpre
? GPOINTER_TO_INT (lpre
->next
->data
) : -1;
3444 GnmStyle
const *spre
= lpre
? lpre
->next
->next
->data
: NULL
;
3445 int cpost
= lpost
? GPOINTER_TO_INT (lpost
->data
) : -1;
3446 int rpost
= lpost
? GPOINTER_TO_INT (lpost
->next
->data
) : -1;
3447 GnmStyle
const *spost
= lpost
? lpost
->next
->next
->data
: NULL
;
3450 if (!spre
|| !spost
) {
3452 g_warning ("Style optimizer failure at end!");
3454 } else if (cpre
!= cpost
|| rpre
!= rpost
) {
3456 g_warning ("Style optimizer position conflict at %s!",
3457 cell_coord_name (cpre
, rpre
));
3459 } else if (!gnm_style_eq (spre
, spost
)) {
3461 g_warning ("Style optimizer failure at %s!",
3462 cell_coord_name (cpre
, rpre
));
3466 if (spre
) gnm_style_unref (spre
);
3467 if (spost
) gnm_style_unref (spost
);
3471 g_slist_free (post
);
3477 sheet_style_optimize (Sheet
*sheet
)
3479 CellTileOptimize data
;
3483 g_return_if_fail (IS_SHEET (sheet
));
3485 if (gnm_debug_flag ("no-style-optimize"))
3488 sheet_colrow_optimize (sheet
);
3490 data
.ss
= gnm_sheet_get_size (sheet
);
3491 data
.recursion
= TRUE
;
3493 if (debug_style_optimize
) {
3494 g_printerr ("Optimizing %s\n", sheet
->name_unquoted
);
3495 cell_tile_dump (&sheet
->style_data
->styles
,
3496 sheet
->tile_top_level
, &data
,
3500 verify
= gnm_debug_flag ("style-optimize-verify");
3501 pre
= verify
? sample_styles (sheet
) : NULL
;
3503 cell_tile_optimize (&sheet
->style_data
->styles
,
3504 sheet
->tile_top_level
, &data
,
3507 if (debug_style_optimize
)
3508 g_printerr ("Optimizing %s...done\n", sheet
->name_unquoted
);
3511 GSList
*post
= sample_styles (sheet
);
3512 verify_styles (pre
, post
);