1.12.39
[gnumeric.git] / src / mstyle.c
blob2dd4e2cde86e93cdd1e10f43d9cd2f75e4bb9559
1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * gnm-style.c: Storing a style
5 * Authors:
6 * Michael Meeks <mmeeks@gnu.org>
7 * Almer S. Tigelaar <almer@gnome.org>
8 * Jody Goldberg <jody@gnome.org>
9 * Morten Welinder <terra@gnome.org>
11 #include <gnumeric-config.h>
12 #include "gnumeric.h"
13 #include "style.h"
15 #include "gnm-style-impl.h"
16 #include "sheet-style.h"
17 #include "style-conditions.h"
18 #include "hlink.h"
19 #include "input-msg.h"
20 #include "application.h"
21 #include "parse-util.h"
22 #include "expr.h"
23 #include "value.h"
24 #include "gutils.h"
25 #include "ranges.h"
26 #include "gnumeric-conf.h"
27 #include <goffice/goffice.h>
28 #include <string.h>
30 #define DEBUG_STYLES
31 #ifndef USE_MSTYLE_POOL
32 #define USE_MSTYLE_POOL 1
33 #endif
35 #if USE_MSTYLE_POOL
36 /* Memory pool for GnmStyles. */
37 static GOMemChunk *gnm_style_pool;
38 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
39 #define CHUNK_ALLOC0(T,p) ((T*)go_mem_chunk_alloc0 (p))
40 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
41 #else
42 #define CHUNK_ALLOC(T,c) g_new (T,1)
43 #define CHUNK_ALLOC0(T,c) g_new0 (T,1)
44 #define CHUNK_FREE(p,v) g_free ((v))
45 #endif
47 #define UNROLLED_FOR(init_,cond_,step_,code_) \
48 do { \
49 init_; \
50 if (cond_) { code_; step_; \
51 if (cond_) { code_; step_; \
52 if (cond_) { code_; step_; \
53 if (cond_) { code_; step_; \
54 if (cond_) { code_; step_; \
55 if (cond_) { code_; step_; \
56 if (cond_) { code_; step_; \
57 if (cond_) { code_; step_; \
58 if (cond_) { code_; step_; \
59 if (cond_) { code_; step_; \
60 if (cond_) { code_; step_; \
61 if (cond_) { code_; step_; \
62 if (cond_) { code_; step_; \
63 if (cond_) { code_; step_; \
64 if (cond_) { code_; step_; \
65 if (cond_) { code_; step_; \
66 if (cond_) { code_; step_; \
67 if (cond_) { code_; step_; \
68 if (cond_) { code_; step_; \
69 if (cond_) { code_; step_; \
70 if (cond_) { code_; step_; \
71 if (cond_) { code_; step_; \
72 if (cond_) { code_; step_; \
73 if (cond_) { code_; step_; \
74 if (cond_) { code_; step_; \
75 if (cond_) { code_; step_; \
76 if (cond_) { code_; step_; \
77 if (cond_) { code_; step_; \
78 if (cond_) { code_; step_; \
79 if (cond_) { code_; step_; \
80 if (cond_) { code_; step_; \
81 if (cond_) { code_; step_; \
82 if (cond_) { code_; step_; \
83 if (cond_) { code_; step_; \
84 if (cond_) { code_; step_; \
85 if (cond_) { code_; step_; \
86 if (cond_) { code_; step_; \
87 if (cond_) { code_; step_; \
88 if (cond_) { code_; step_; \
89 if (cond_) { code_; step_; \
90 if (cond_) { code_; step_; \
91 if (cond_) { code_; step_; \
92 if (cond_) { code_; step_; \
93 if (cond_) { code_; step_; \
94 if (cond_) { code_; step_; \
95 if (cond_) { code_; step_; \
96 if (cond_) { code_; step_; \
97 if (cond_) { code_; step_; \
98 if (cond_) { code_; step_; \
99 if (cond_) { code_; step_; \
100 g_assert_not_reached (); \
101 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} \
102 } while (0)
106 static char const * const
107 gnm_style_element_name[MSTYLE_ELEMENT_MAX] = {
108 "Color.Back",
109 "Color.Pattern",
110 "Border.Top",
111 "Border.Bottom",
112 "Border.Left",
113 "Border.Right",
114 "Border.RevDiagonal",
115 "Border.Diagonal",
116 "Pattern",
117 "Color.Fore",
118 "Font.Name",
119 "Font.Bold",
120 "Font.Italic",
121 "Font.Underline",
122 "Font.Strikethrough",
123 "Font.Script",
124 "Font.Size",
125 "Format",
126 "Align.v",
127 "Align.h",
128 "Indent",
129 "Rotation",
130 "WrapText",
131 "ShrinkToFit",
132 "Contents.Locked",
133 "Contents.Hidden",
134 "Validation",
135 "Hyper Link",
136 "Input Msg"
139 /* Some ref/link count debugging */
140 #if 0
141 #define d(arg) g_printerr arg
142 #else
143 #define d(arg) do { } while (0)
144 #endif
146 static void
147 clear_conditional_merges (GnmStyle *style)
149 if (style->cond_styles) {
150 unsigned i = style->cond_styles->len;
151 while (i-- > 0)
152 gnm_style_unref (g_ptr_array_index (style->cond_styles, i));
153 g_ptr_array_free (style->cond_styles, TRUE);
154 style->cond_styles = NULL;
158 #define MIX(H) do { \
159 H *= G_GUINT64_CONSTANT(123456789012345); \
160 H ^= (H >> 31); \
161 } while (0)
163 static void
164 gnm_style_update (GnmStyle *style)
166 guint64 hash = 0;
167 int i;
169 g_return_if_fail (style->changed);
171 style->changed = 0;
173 clear_conditional_merges (style);
174 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
175 style->cond_styles = gnm_style_conditions_overlay (style->conditions, style);
177 /* ---------------------------------------- */
179 if (elem_is_set (style, MSTYLE_COLOR_BACK)) {
180 if (!style->color.back->is_auto)
181 hash ^= GPOINTER_TO_UINT (style->color.back);
182 else
183 hash++;
185 MIX (hash);
187 if (elem_is_set (style, MSTYLE_COLOR_PATTERN)) {
188 if (!style->color.pattern->is_auto)
189 hash ^= GPOINTER_TO_UINT (style->color.pattern);
190 else
191 hash++;
193 MIX (hash);
195 if (elem_is_set (style, MSTYLE_FONT_COLOR)) {
196 if (!style->color.font->is_auto)
197 hash ^= GPOINTER_TO_UINT (style->color.font);
198 else
199 hash++;
201 MIX (hash);
203 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
204 if (elem_is_set (style, i))
205 hash ^= GPOINTER_TO_UINT (style->borders[i - MSTYLE_BORDER_TOP]);
206 else
207 hash++;
208 MIX (hash);
211 if (elem_is_set (style, MSTYLE_PATTERN))
212 hash ^= style->pattern;
213 MIX (hash);
215 if (elem_is_set (style, MSTYLE_FONT_NAME))
216 hash ^= GPOINTER_TO_UINT (style->font_detail.name);
217 MIX (hash);
219 if (elem_is_set (style, MSTYLE_FONT_BOLD))
220 hash ^= (style->font_detail.bold ? 1 : 2);
221 MIX (hash);
223 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
224 hash ^= (style->font_detail.italic ? 1 : 2);
225 MIX (hash);
227 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
228 hash ^= (style->font_detail.underline ? 1 : 2);
229 MIX (hash);
231 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
232 hash ^= (style->font_detail.strikethrough ? 1 : 2);
233 MIX (hash);
235 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
236 hash ^= (style->font_detail.script + 0x100);
237 MIX (hash);
239 if (elem_is_set (style, MSTYLE_FONT_SIZE))
240 hash ^= ((int)(style->font_detail.size * 97));
241 MIX (hash);
243 if (elem_is_set (style, MSTYLE_FORMAT))
244 hash ^= GPOINTER_TO_UINT (style->format);
245 MIX (hash);
247 if (elem_is_set (style, MSTYLE_ALIGN_H))
248 hash ^= (style->h_align + 0x100);
249 MIX (hash);
251 if (elem_is_set (style, MSTYLE_ALIGN_V))
252 hash ^= (style->v_align + 0x100);
253 MIX (hash);
255 if (elem_is_set (style, MSTYLE_INDENT))
256 hash ^= style->indent;
257 MIX (hash);
259 if (elem_is_set (style, MSTYLE_ROTATION))
260 hash ^= style->rotation;
261 MIX (hash);
263 if (elem_is_set (style, MSTYLE_TEXT_DIR))
264 hash ^= (style->text_dir + 0x100);
265 MIX (hash);
267 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
268 hash ^= (style->wrap_text ? 1 : 2);
269 MIX (hash);
271 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
272 hash ^= (style->shrink_to_fit ? 1 : 2);
273 MIX (hash);
275 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
276 hash ^= (style->contents_locked ? 1 : 2);
277 MIX (hash);
279 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
280 hash ^= (style->contents_hidden ? 1 : 2);
281 MIX (hash);
283 style->hash_key_xl = (guint32)hash;
285 /* From here on, fields are not in MS XL */
287 if (elem_is_set (style, MSTYLE_VALIDATION)) {
289 * The hash used must not depend on the expressions inside
290 * the validation.
292 hash ^= (style->validation != NULL ? 1 : 2);
294 MIX (hash);
296 if (elem_is_set (style, MSTYLE_HLINK))
297 hash ^= GPOINTER_TO_UINT (style->hlink);
298 MIX (hash);
300 if (elem_is_set (style, MSTYLE_INPUT_MSG))
301 hash ^= GPOINTER_TO_UINT (style->input_msg);
302 MIX (hash);
304 if (elem_is_set (style, MSTYLE_CONDITIONS)) {
306 * The hash used must not depend on the expressions inside
307 * the conditions.
309 hash ^= style->conditions
310 ? gnm_style_conditions_hash (style->conditions)
311 : 1u;
313 MIX (hash);
315 style->hash_key = (guint32)hash;
317 if (G_UNLIKELY (style->set == 0)) {
319 * gnm_style_new and gnm_style_dup both assume that the
320 * correct hash values (both of them) for the empty style
321 * is zero.
323 g_assert (style->hash_key == 0);
324 g_assert (style->hash_key_xl == 0);
328 #undef MIX
330 guint
331 gnm_style_hash_XL (gconstpointer style)
333 if (((GnmStyle const *)style)->changed)
334 gnm_style_update ((GnmStyle *)style);
335 return ((GnmStyle const *)style)->hash_key_xl;
338 guint
339 gnm_style_hash (gconstpointer style)
341 if (((GnmStyle const *)style)->changed)
342 gnm_style_update ((GnmStyle *)style);
343 return ((GnmStyle const *)style)->hash_key;
346 #define ELEM_IS_EQ(a,b,elem) \
347 (elem == MSTYLE_COLOR_BACK \
348 ? a->color.back == b->color.back || (a->color.back->is_auto && b->color.back->is_auto) \
349 : (elem == MSTYLE_COLOR_PATTERN \
350 ? a->color.pattern == b->color.pattern || (a->color.pattern->is_auto && b->color.pattern->is_auto) \
351 : (elem >= MSTYLE_BORDER_TOP && elem <= MSTYLE_BORDER_DIAGONAL) \
352 ? a->borders[elem - MSTYLE_BORDER_TOP] == b->borders[elem - MSTYLE_BORDER_TOP] \
353 : (elem == MSTYLE_PATTERN \
354 ? a->pattern == b->pattern \
355 : (elem == MSTYLE_FONT_COLOR \
356 ? a->color.font == b->color.font || (a->color.font->is_auto && b->color.font->is_auto) \
357 : (elem == MSTYLE_FONT_NAME \
358 ? a->font_detail.name == b->font_detail.name \
359 : (elem == MSTYLE_FONT_BOLD \
360 ? a->font_detail.bold == b->font_detail.bold \
361 : (elem == MSTYLE_FONT_ITALIC \
362 ? a->font_detail.italic == b->font_detail.italic \
363 : (elem == MSTYLE_FONT_UNDERLINE \
364 ? a->font_detail.underline == b->font_detail.underline \
365 : (elem == MSTYLE_FONT_STRIKETHROUGH \
366 ? a->font_detail.strikethrough == b->font_detail.strikethrough \
367 : (elem == MSTYLE_FONT_SCRIPT \
368 ? a->font_detail.script == b->font_detail.script \
369 : (elem == MSTYLE_FONT_SIZE \
370 ? a->font_detail.size == b->font_detail.size \
371 : (elem == MSTYLE_FORMAT \
372 ? a->format == b->format \
373 : (elem == MSTYLE_ALIGN_V \
374 ? a->v_align == b->v_align \
375 : (elem == MSTYLE_ALIGN_H \
376 ? a->h_align == b->h_align \
377 : (elem == MSTYLE_INDENT \
378 ? a->indent == b->indent \
379 : (elem == MSTYLE_ROTATION \
380 ? a->rotation == b->rotation \
381 : (elem == MSTYLE_TEXT_DIR \
382 ? a->text_dir == b->text_dir \
383 : (elem == MSTYLE_WRAP_TEXT \
384 ? a->wrap_text == b->wrap_text \
385 : (elem == MSTYLE_SHRINK_TO_FIT \
386 ? a->shrink_to_fit == b->shrink_to_fit \
387 : (elem == MSTYLE_CONTENTS_LOCKED \
388 ? a->contents_locked == b->contents_locked \
389 : (elem == MSTYLE_CONTENTS_HIDDEN \
390 ? a->contents_hidden == b->contents_hidden \
391 : (elem == MSTYLE_VALIDATION \
392 ? a->validation == b->validation \
393 : (elem == MSTYLE_HLINK \
394 ? a->hlink == b->hlink \
395 : (elem == MSTYLE_INPUT_MSG \
396 ? a->input_msg == b->input_msg \
397 : (elem == MSTYLE_CONDITIONS \
398 ? (a->conditions == b->conditions || \
399 (a->conditions && b->conditions && \
400 gnm_style_conditions_equal (a->conditions, b->conditions, FALSE))) \
401 : FALSE)))))))))))))))))))))))))
404 * Note: the above is suboptimal for validation, hlink, input_msg.
406 * We are comparing pointers (which at least safely matches what we do
407 * with the hash), but I think we want proper equality.
410 static gboolean
411 elem_is_eq (GnmStyle const *a, GnmStyle const *b, GnmStyleElement elem)
413 return ELEM_IS_EQ (a, b, elem);
416 static void
417 elem_assign_contents (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
419 #ifdef DEBUG_STYLES
420 g_return_if_fail (src != dst);
421 g_return_if_fail (elem_is_set (src, elem));
422 #endif
423 switch (elem) {
424 case MSTYLE_COLOR_BACK : style_color_ref (dst->color.back = src->color.back); return;
425 case MSTYLE_COLOR_PATTERN : style_color_ref (dst->color.pattern = src->color.pattern); return;
426 case MSTYLE_ANY_BORDER:
427 elem -= MSTYLE_BORDER_TOP;
428 gnm_style_border_ref (dst->borders[elem] = src->borders[elem]);
429 return;
430 case MSTYLE_PATTERN: dst->pattern = src->pattern; return;
431 case MSTYLE_FONT_COLOR : style_color_ref (dst->color.font = src->color.font); return;
432 case MSTYLE_FONT_NAME: go_string_ref (dst->font_detail.name = src->font_detail.name); return;
433 case MSTYLE_FONT_BOLD: dst->font_detail.bold = src->font_detail.bold; return;
434 case MSTYLE_FONT_ITALIC: dst->font_detail.italic = src->font_detail.italic; return;
435 case MSTYLE_FONT_UNDERLINE: dst->font_detail.underline = src->font_detail.underline; return;
436 case MSTYLE_FONT_STRIKETHROUGH: dst->font_detail.strikethrough = src->font_detail.strikethrough; return;
437 case MSTYLE_FONT_SCRIPT: dst->font_detail.script = src->font_detail.script; return;
438 case MSTYLE_FONT_SIZE: dst->font_detail.size = src->font_detail.size; return;
439 case MSTYLE_FORMAT: go_format_ref (dst->format = src->format); return;
440 case MSTYLE_ALIGN_V: dst->v_align = src->v_align; return;
441 case MSTYLE_ALIGN_H: dst->h_align = src->h_align; return;
442 case MSTYLE_INDENT: dst->indent = src->indent; return;
443 case MSTYLE_ROTATION: dst->rotation = src->rotation; return;
444 case MSTYLE_TEXT_DIR: dst->text_dir = src->text_dir; return;
445 case MSTYLE_WRAP_TEXT: dst->wrap_text = src->wrap_text; return;
446 case MSTYLE_SHRINK_TO_FIT: dst->shrink_to_fit = src->shrink_to_fit; return;
447 case MSTYLE_CONTENTS_LOCKED: dst->contents_locked = src->contents_locked; return;
448 case MSTYLE_CONTENTS_HIDDEN: dst->contents_hidden = src->contents_hidden; return;
449 case MSTYLE_VALIDATION:
450 if ((dst->validation = src->validation))
451 gnm_validation_ref (dst->validation);
452 return;
453 case MSTYLE_HLINK:
454 if ((dst->hlink = src->hlink))
455 g_object_ref (dst->hlink);
456 return;
457 case MSTYLE_INPUT_MSG:
458 if ((dst->input_msg = src->input_msg))
459 g_object_ref (dst->input_msg);
460 return;
461 case MSTYLE_CONDITIONS:
462 if ((dst->conditions = src->conditions))
463 g_object_ref (dst->conditions);
464 return;
465 default:
470 static void
471 elem_clear_contents (GnmStyle *style, GnmStyleElement elem)
473 #ifdef DEBUG_STYLES
474 g_return_if_fail (style != NULL);
475 #endif
476 if (!elem_is_set (style, elem))
477 return;
479 switch (elem) {
480 case MSTYLE_COLOR_BACK : style_color_unref (style->color.back); return;
481 case MSTYLE_COLOR_PATTERN : style_color_unref (style->color.pattern); return;
482 case MSTYLE_ANY_BORDER:
483 gnm_style_border_unref (style->borders[elem - MSTYLE_BORDER_TOP]);
484 return;
485 case MSTYLE_FONT_COLOR : style_color_unref (style->color.font); return;
486 case MSTYLE_FONT_NAME: go_string_unref (style->font_detail.name); return;
487 case MSTYLE_FORMAT: go_format_unref (style->format); return;
488 case MSTYLE_VALIDATION:
489 if (style->validation)
490 gnm_validation_unref (style->validation);
491 return;
492 case MSTYLE_HLINK:
493 if (style->hlink)
494 g_object_unref (style->hlink);
495 return;
496 case MSTYLE_INPUT_MSG:
497 if (style->input_msg)
498 g_object_unref (style->input_msg);
499 return;
500 case MSTYLE_CONDITIONS:
501 if (style->conditions) {
502 clear_conditional_merges (style);
503 g_object_unref (style->conditions);
505 return;
506 default:
512 * gnm_style_find_conflicts :
513 * @accum: accumulator #GnmStyle
514 * @overlay: #GnmStyle
515 * @conflicts: flags
517 * Copy any items from @overlay that do not conflict with the values in @accum.
518 * If an element had a previous conflict (flagged via @conflicts) it is ignored.
520 * Returns @conflicts with any new conflicts added.
522 unsigned int
523 gnm_style_find_conflicts (GnmStyle *accum, GnmStyle const *overlay,
524 unsigned int conflicts)
526 int i;
528 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (conflicts));
530 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
531 if (conflicts & (1u << i) || !elem_is_set (overlay, i)) {
532 /* Nothing */
533 } else if (!elem_is_set (accum, i)) {
534 elem_assign_contents (accum, overlay, i);
535 elem_set (accum, i);
536 elem_changed (accum, i);
537 } else if (!elem_is_eq (accum, overlay, i))
538 conflicts |= (1u << i);
541 return conflicts;
544 #define GNM_INPUT_MSG_EQUAL3(a,b,r) (gnm_input_msg_equal (a,b))
546 #define RELAX_CHECK(op_,field_,checker_) do { \
547 if (diffs & (1u << (op_)) && \
548 elem_is_set (a, (op_)) && \
549 elem_is_set (b, (op_)) && \
550 ((a->field_ == NULL) != (b->field_ == NULL) || \
551 checker_ (a->field_, b->field_, relax_sheet))) \
552 diffs &= ~(1u << (op_)); \
553 } while (0)
556 * gnm_style_find_differences:
557 * @a: A #GnmStyle
558 * @b: A #GnmStyle
559 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
561 * Determine how two fully-qualified styles differ.
563 * Returns differences as a bitset of #GnmStyleElement.
565 unsigned int
566 gnm_style_find_differences (GnmStyle const *a, GnmStyle const *b,
567 gboolean relax_sheet)
569 int i;
570 unsigned int diffs = 0;
572 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (diffs));
574 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
575 if (elem_is_set (a, i) != elem_is_set (b, i) ||
576 (elem_is_set (a, i) && !elem_is_eq (a, b, i)))
577 diffs |= (1u << i);
580 if (relax_sheet) {
581 RELAX_CHECK (MSTYLE_HLINK, hlink, gnm_hlink_equal);
582 RELAX_CHECK (MSTYLE_VALIDATION, validation, gnm_validation_equal);
583 RELAX_CHECK (MSTYLE_INPUT_MSG, input_msg, GNM_INPUT_MSG_EQUAL3);
584 RELAX_CHECK (MSTYLE_CONDITIONS, conditions, gnm_style_conditions_equal);
587 return diffs;
590 #undef RELAX_CHECK
591 #undef GNM_INPUT_MSG_EQUAL3
593 static inline void
594 gnm_style_clear_pango (GnmStyle *style)
596 if (style->pango_attrs) {
597 pango_attr_list_unref (style->pango_attrs);
598 style->pango_attrs = NULL;
603 static inline void
604 gnm_style_clear_font (GnmStyle *style)
606 if (style->font) {
607 gnm_font_unref (style->font);
608 style->font = NULL;
610 g_clear_object (&style->font_context);
614 * gnm_style_new :
616 * Caller is responsible for unrefing the result.
618 * Returns a new style with _no_ elements set.
620 GnmStyle *
621 gnm_style_new (void)
623 GnmStyle *style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
625 style->ref_count = 1;
626 style->link_count = 0;
627 style->linked_sheet = NULL;
628 style->pango_attrs = NULL;
629 style->font = NULL;
630 style->validation = NULL;
632 style->set = style->changed = 0;
633 style->validation = NULL;
634 style->hlink = NULL;
635 style->input_msg = NULL;
636 style->conditions = NULL;
638 d(("new %p\n", style));
640 return style;
644 * gnm_style_new_default:
646 * Caller is responsible for unrefing the result.
648 * Return value: a new style initialized to the default state.
650 GnmStyle *
651 gnm_style_new_default (void)
653 GnmStyle *new_style = gnm_style_new ();
654 int i;
656 gnm_style_set_font_name (new_style, gnm_conf_get_core_defaultfont_name ());
657 gnm_style_set_font_size (new_style, gnm_conf_get_core_defaultfont_size ());
658 gnm_style_set_font_bold (new_style, gnm_conf_get_core_defaultfont_bold ());
659 gnm_style_set_font_italic (new_style, gnm_conf_get_core_defaultfont_italic ());
661 gnm_style_set_format (new_style, go_format_general ());
662 gnm_style_set_align_v (new_style, GNM_VALIGN_BOTTOM);
663 gnm_style_set_align_h (new_style, GNM_HALIGN_GENERAL);
664 gnm_style_set_indent (new_style, 0);
665 gnm_style_set_rotation (new_style, 0);
666 gnm_style_set_text_dir (new_style, GNM_TEXT_DIR_CONTEXT);
667 gnm_style_set_wrap_text (new_style, FALSE);
668 gnm_style_set_shrink_to_fit (new_style, FALSE);
669 gnm_style_set_contents_locked (new_style, TRUE);
670 gnm_style_set_contents_hidden (new_style, FALSE);
671 gnm_style_set_font_uline (new_style, UNDERLINE_NONE);
672 gnm_style_set_font_strike (new_style, FALSE);
673 gnm_style_set_font_script (new_style, GO_FONT_SCRIPT_STANDARD);
675 gnm_style_set_validation (new_style, NULL);
676 gnm_style_set_hlink (new_style, NULL);
677 gnm_style_set_input_msg (new_style, NULL);
678 gnm_style_set_conditions (new_style, NULL);
680 gnm_style_set_font_color (new_style, style_color_black ());
681 gnm_style_set_back_color (new_style, style_color_auto_back ());
682 gnm_style_set_pattern_color (new_style, style_color_black ());
684 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
685 gnm_style_set_border (new_style, i,
686 gnm_style_border_ref (gnm_style_border_none ()));
687 gnm_style_set_pattern (new_style, 0);
689 return new_style;
692 GnmStyle *
693 gnm_style_dup (GnmStyle const *src)
695 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
696 int i;
698 new_style->ref_count = 1;
699 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
700 if (elem_is_set (src, i)) {
701 elem_assign_contents (new_style, src, i);
702 elem_set (new_style, i);
703 elem_changed (new_style, i);
706 if ((new_style->pango_attrs = src->pango_attrs)) {
707 pango_attr_list_ref (new_style->pango_attrs);
708 new_style->pango_attrs_zoom = src->pango_attrs_zoom;
711 if ((new_style->font = src->font)) {
712 gnm_font_ref (new_style->font);
713 new_style->font_context = g_object_ref (src->font_context);
716 d(("dup %p\n", new_style));
717 return new_style;
721 * gnm_style_new_merged :
722 * @base: #GnmStyle
723 * @overlay: #GnmStyle
725 * A new GnmStyle that contains any elements of @overlay that are set, and uses
726 * @base for anything that is not set in @overlay.
728 * Returns: (transfer full): A ref to a new GnmStyle.
730 GnmStyle *
731 gnm_style_new_merged (GnmStyle const *base, GnmStyle const *overlay)
733 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
734 int i;
736 new_style->ref_count = 1;
737 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
738 if (elem_is_set (overlay, i))
739 elem_assign_contents (new_style, overlay, i);
740 else if (elem_is_set (base, i))
741 elem_assign_contents (new_style, base, i);
742 else
743 continue;
744 elem_set (new_style, i);
745 elem_changed (new_style, i);
747 d(("copy merge %p\n", new_style));
748 return new_style;
751 void
752 gnm_style_ref (GnmStyle const *style)
754 g_return_if_fail (style != NULL);
755 g_return_if_fail (style->ref_count > 0);
757 ((GnmStyle *)style)->ref_count++;
758 d(("ref %p = %d\n", style, style->ref_count));
762 * gnm_style_unref :
763 * @style: #GnmStyle const
765 * Unrefs and _potentially frees_ @style.
766 * Takes a _const_ pointer to facilitate life cycles. The const indicates that
767 * the content can not be changed, mainly when handling styles that are in the
768 * style hash.
770 void
771 gnm_style_unref (GnmStyle const *style)
773 g_return_if_fail (style != NULL);
774 g_return_if_fail (style->ref_count > 0);
776 d(("unref %p = %d\n", style, style->ref_count-1));
777 if (((GnmStyle *)style)->ref_count-- <= 1) {
778 GnmStyle *unconst = (GnmStyle *)style;
779 int i;
781 g_return_if_fail (style->link_count == 0);
782 g_return_if_fail (style->linked_sheet == NULL);
784 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
785 elem_clear_contents (unconst, i);
786 unconst->set = 0;
787 clear_conditional_merges (unconst);
788 gnm_style_clear_pango (unconst);
789 gnm_style_clear_font (unconst);
791 if (style->deps) {
792 if (style->deps->len > 0)
793 g_warning ("Leftover style deps!");
794 g_ptr_array_free (style->deps, TRUE);
797 CHUNK_FREE (gnm_style_pool, unconst);
801 GType
802 gnm_style_get_type (void)
804 static GType t = 0;
806 if (t == 0) {
807 t = g_boxed_type_register_static ("GnmStyle",
808 (GBoxedCopyFunc)gnm_style_ref,
809 (GBoxedFreeFunc)gnm_style_unref);
811 return t;
815 * Replace auto pattern color in style with sheet's auto pattern color.
816 * make_copy tells if we are allowed to modify the style in place or we must
817 * make a copy first.
819 static GnmStyle *
820 link_pattern_color (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
822 GnmColor *pattern_color = style->color.pattern;
824 if (pattern_color->is_auto && auto_color != pattern_color) {
825 style_color_ref (auto_color);
826 if (make_copy) {
827 GnmStyle *orig = style;
828 style = gnm_style_dup (style);
829 gnm_style_unref (orig);
831 gnm_style_set_pattern_color (style, auto_color);
833 return style;
837 * Replace auto border colors in style with sheet's auto pattern
838 * color. (pattern is *not* a typo.)
839 * make_copy tells if we are allowed to modify the style in place or we must
840 * make a copy first.
842 * FIXME: We conjecture that XL color 64 in border should change with the
843 * pattern, but not color 127. That distinction is not yet represented in
844 * our data structures.
846 static GnmStyle *
847 link_border_colors (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
849 int i;
851 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i) {
852 if (elem_is_set (style, i)) {
853 GnmBorder *border =
854 style->borders[i- MSTYLE_BORDER_TOP];
855 GnmColor *color;
857 if (!border)
858 continue;
860 color = border->color;
861 if (color->is_auto && auto_color != color) {
862 GnmBorder *new_border;
863 GnmStyleBorderOrientation orientation;
865 switch (i) {
866 case MSTYLE_BORDER_LEFT:
867 case MSTYLE_BORDER_RIGHT:
868 orientation = GNM_STYLE_BORDER_VERTICAL;
869 break;
870 case MSTYLE_BORDER_REV_DIAGONAL:
871 case MSTYLE_BORDER_DIAGONAL:
872 orientation = GNM_STYLE_BORDER_DIAGONAL;
873 break;
874 case MSTYLE_BORDER_TOP:
875 case MSTYLE_BORDER_BOTTOM:
876 default:
877 orientation = GNM_STYLE_BORDER_HORIZONTAL;
878 break;
880 style_color_ref (auto_color);
881 new_border = gnm_style_border_fetch (
882 border->line_type, auto_color,
883 orientation);
885 if (make_copy) {
886 GnmStyle *orig = style;
887 style = gnm_style_dup (style);
888 gnm_style_unref (orig);
889 make_copy = FALSE;
891 gnm_style_set_border (style, i, new_border);
895 return style;
898 static void
899 gnm_style_linked_sheet_changed (GnmStyle *style)
901 Sheet *sheet = style->linked_sheet;
903 if (elem_is_set (style, MSTYLE_VALIDATION) &&
904 style->validation &&
905 gnm_validation_get_sheet (style->validation) != sheet) {
906 GnmValidation *new_v = gnm_validation_dup (style->validation);
907 gnm_validation_set_sheet (new_v, sheet);
908 gnm_style_set_validation (style, new_v);
911 if (elem_is_set (style, MSTYLE_HLINK) &&
912 style->hlink &&
913 gnm_hlink_get_sheet (style->hlink) != sheet) {
914 GnmHLink *new_l = gnm_hlink_dup (style->hlink);
915 gnm_hlink_set_sheet (new_l, sheet);
916 gnm_style_set_hlink (style, new_l);
919 if (elem_is_set (style, MSTYLE_CONDITIONS) &&
920 style->conditions &&
921 gnm_style_conditions_get_sheet (style->conditions) != sheet) {
922 GnmStyleConditions *new_c = gnm_style_conditions_dup (style->conditions);
923 gnm_style_conditions_set_sheet (new_c, sheet);
924 gnm_style_set_conditions (style, new_c);
929 * gnm_style_link_sheet :
930 * @style:
931 * @sheet:
933 * ABSORBS a reference to the style and sets the link count to 1.
935 * Where auto pattern color occurs in the style (it may for pattern and
936 * borders), it is replaced with the sheet's auto pattern color. We make
937 * sure that we do not modify the style which was passed in to us, but also
938 * that we don't copy more than once. The final argument to the
939 * link_xxxxx_color functions tell whether or not to copy.
941 GnmStyle *
942 gnm_style_link_sheet (GnmStyle *style, Sheet *sheet)
944 GnmColor *auto_color;
945 gboolean style_is_orig = TRUE;
947 if (style->linked_sheet != NULL) {
948 GnmStyle *orig = style;
949 style = gnm_style_dup (style);
950 gnm_style_unref (orig);
951 style_is_orig = FALSE;
953 /* safety test */
954 g_return_val_if_fail (style->linked_sheet != sheet, style);
957 g_return_val_if_fail (style->link_count == 0, style);
958 g_return_val_if_fail (style->linked_sheet == NULL, style);
960 auto_color = sheet_style_get_auto_pattern_color (sheet);
961 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
962 style = link_pattern_color (style, auto_color, style_is_orig);
963 style = link_border_colors (style, auto_color, style_is_orig);
964 style_color_unref (auto_color);
966 style->linked_sheet = sheet;
967 style->link_count = 1;
969 gnm_style_linked_sheet_changed (style);
971 d(("link sheet %p = 1\n", style));
972 return style;
975 void
976 gnm_style_link (GnmStyle *style)
978 g_return_if_fail (style->link_count > 0);
980 style->link_count++;
981 d(("link %p = %d\n", style, style->link_count));
984 void
985 gnm_style_link_multiple (GnmStyle *style, int count)
987 g_return_if_fail (style->link_count > 0);
989 style->link_count += count;
990 d(("multiple link %p + %d = %d\n", style, count, style->link_count));
993 void
994 gnm_style_unlink (GnmStyle *style)
996 g_return_if_fail (style->link_count > 0);
998 d(("unlink %p = %d\n", style, style->link_count-1));
999 if (style->link_count-- == 1) {
1000 sheet_style_unlink (style->linked_sheet, style);
1001 style->linked_sheet = NULL;
1002 gnm_style_unref (style);
1006 gboolean
1007 gnm_style_eq (GnmStyle const *a, GnmStyle const *b)
1009 return a == b;
1012 gboolean
1013 gnm_style_equal (GnmStyle const *a, GnmStyle const *b)
1015 int i;
1017 if (a == b)
1018 return TRUE;
1019 if (a->set != b->set || !gnm_style_equal_XL (a, b))
1020 return FALSE;
1021 UNROLLED_FOR (i = MSTYLE_VALIDATION, i < MSTYLE_ELEMENT_MAX, i++, {
1022 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1023 return FALSE;
1026 return TRUE;
1029 gboolean
1030 gnm_style_equal_XL (GnmStyle const *a, GnmStyle const *b)
1032 int i;
1034 g_return_val_if_fail (a != NULL, FALSE);
1035 g_return_val_if_fail (b != NULL, FALSE);
1037 if (a == b)
1038 return TRUE;
1040 if ((a->set ^ b->set) & ((1u << MSTYLE_VALIDATION) - 1))
1041 return FALSE;
1043 UNROLLED_FOR (i = MSTYLE_COLOR_BACK, i < MSTYLE_VALIDATION, i++, {
1044 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1045 return FALSE;
1047 return TRUE;
1051 * gnm_style_equal_elem:
1052 * @a: first style
1053 * @b: second style
1054 * @e: style element
1056 * Returns: %TRUE, if the two styles have the same contents for the
1057 * given element, either because neither have it set, or because both
1058 * have it set and to the same value.
1060 gboolean
1061 gnm_style_equal_elem (GnmStyle const *a, GnmStyle const *b, GnmStyleElement e)
1063 if (elem_is_set (a, e))
1064 return elem_is_set (b, e) && elem_is_eq (a, b, e);
1065 else
1066 return !elem_is_set (b, e);
1071 #define CMP_TRY_NUMBER_RAW(a_,b_) \
1072 do { \
1073 if ((a_) < (b_)) return -1; \
1074 if ((a_) > (b_)) return -1; \
1075 } while (0)
1077 #define CMP_TRY_NUMBER(e_,f_) \
1078 do { \
1079 if (elem_is_set (a, (e_))) \
1080 CMP_TRY_NUMBER_RAW(a->f_, b->f_); \
1081 } while (0)
1083 #define CMP_TRY_COLOR(e_,f_) \
1084 do { \
1085 if (elem_is_set (a, (e_))) { \
1086 CMP_TRY_NUMBER_RAW(a->f_->is_auto, b->f_->is_auto); \
1087 CMP_TRY_NUMBER_RAW(a->f_->go_color, b->f_->go_color); \
1089 } while (0)
1092 * Ordering of GnmStyles. Apart from FIXMEs, this shouldn't change
1093 * from one run to the next.
1096 gnm_style_cmp (GnmStyle const *a, GnmStyle const *b)
1098 GnmStyleElement e;
1100 if (a == b)
1101 return 0;
1104 * Very quick comparison based on what is set. This also allows
1105 * us to check on one elem_is_set below.
1107 CMP_TRY_NUMBER_RAW (a->set, b->set);
1109 CMP_TRY_COLOR (MSTYLE_FONT_COLOR, color.font);
1110 CMP_TRY_COLOR (MSTYLE_COLOR_BACK, color.back);
1111 CMP_TRY_COLOR (MSTYLE_COLOR_PATTERN, color.pattern);
1112 for (e = MSTYLE_BORDER_TOP; e <= MSTYLE_BORDER_DIAGONAL; e++) {
1113 GnmBorder const *ba, *bb;
1114 if (!elem_is_set (a, e))
1115 continue;
1116 ba = a->borders[e - MSTYLE_BORDER_TOP];
1117 bb = b->borders[e - MSTYLE_BORDER_TOP];
1118 if (ba == bb)
1119 continue; /* Handles both being NULL */
1120 CMP_TRY_NUMBER_RAW(!!ba, !!bb);
1121 CMP_TRY_NUMBER_RAW(ba->line_type, bb->line_type);
1122 CMP_TRY_NUMBER_RAW(ba->color->go_color, bb->color->go_color);
1123 CMP_TRY_NUMBER_RAW(ba->begin_margin, bb->begin_margin);
1124 CMP_TRY_NUMBER_RAW(ba->end_margin, bb->end_margin);
1125 CMP_TRY_NUMBER_RAW(ba->width, bb->width);
1127 CMP_TRY_NUMBER (MSTYLE_PATTERN, pattern);
1128 if (elem_is_set (a, MSTYLE_FONT_NAME)) {
1129 /* Plain strcmp, not utf-8. We need to see diffs. */
1130 int tmp = strcmp (a->font_detail.name->str,
1131 b->font_detail.name->str);
1132 if (tmp)
1133 return tmp;
1135 CMP_TRY_NUMBER (MSTYLE_FONT_BOLD, font_detail.bold);
1136 CMP_TRY_NUMBER (MSTYLE_FONT_ITALIC, font_detail.italic);
1137 CMP_TRY_NUMBER (MSTYLE_FONT_UNDERLINE, font_detail.underline);
1138 CMP_TRY_NUMBER (MSTYLE_FONT_STRIKETHROUGH, font_detail.strikethrough);
1139 CMP_TRY_NUMBER (MSTYLE_FONT_SCRIPT, font_detail.script);
1140 CMP_TRY_NUMBER (MSTYLE_FONT_SIZE, font_detail.size);
1141 if (elem_is_set (a, MSTYLE_FORMAT)) {
1142 /* Plain strcmp, not utf-8. We need to see diffs. */
1143 int tmp = strcmp (go_format_as_XL (a->format),
1144 go_format_as_XL (b->format));
1145 if (tmp)
1146 return tmp;
1148 CMP_TRY_NUMBER (MSTYLE_ALIGN_H, h_align);
1149 CMP_TRY_NUMBER (MSTYLE_ALIGN_V, v_align);
1150 CMP_TRY_NUMBER (MSTYLE_INDENT, indent);
1151 CMP_TRY_NUMBER (MSTYLE_ROTATION, rotation);
1152 CMP_TRY_NUMBER (MSTYLE_TEXT_DIR, text_dir);
1153 CMP_TRY_NUMBER (MSTYLE_WRAP_TEXT, wrap_text);
1154 CMP_TRY_NUMBER (MSTYLE_SHRINK_TO_FIT, shrink_to_fit);
1155 CMP_TRY_NUMBER (MSTYLE_CONTENTS_LOCKED, contents_locked);
1156 CMP_TRY_NUMBER (MSTYLE_CONTENTS_HIDDEN, contents_hidden);
1157 /* FIXME: validation */
1158 /* FIXME: hlink */
1159 /* FIXME: input_msg */
1160 /* FIXME: conditions */
1161 /* FIXME: cond_styles */
1163 /* Last resort: pointer comparison. */
1164 return a < b ? -1 : +1;
1167 #undef CMP_TRY_NUMBER
1168 #undef CMP_TRY_COLOR
1172 * gnm_style_equal_header :
1173 * @a: #GnmStyle
1174 * @b: #GnmStyle
1175 * @top: is this a header vertically or horizontally
1177 * Check to see if @a is different enough from @b to make us think that @a is
1178 * from a header.
1180 gboolean
1181 gnm_style_equal_header (GnmStyle const *a, GnmStyle const *b, gboolean top)
1183 int i = top ? MSTYLE_BORDER_BOTTOM : MSTYLE_BORDER_RIGHT;
1185 if (!elem_is_eq (a, b, i))
1186 return FALSE;
1187 for (i = MSTYLE_COLOR_BACK; i <= MSTYLE_COLOR_PATTERN ; i++)
1188 if (!elem_is_eq (a, b, i))
1189 return FALSE;
1190 for (i = MSTYLE_FONT_COLOR; i <= MSTYLE_SHRINK_TO_FIT ; i++)
1191 if (!elem_is_eq (a, b, i))
1192 return FALSE;
1193 return TRUE;
1197 gboolean
1198 gnm_style_is_element_set (GnmStyle const *style, GnmStyleElement elem)
1200 g_return_val_if_fail (style != NULL, FALSE);
1201 g_return_val_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX, FALSE);
1202 return elem_is_set (style, elem);
1206 * gnm_style_is_complete :
1207 * @style: #GnmStyle
1209 * Returns TRUE if all elements are set.
1211 gboolean
1212 gnm_style_is_complete (GnmStyle const *style)
1214 g_return_val_if_fail (style != NULL, FALSE);
1216 return style->set == ((1u << MSTYLE_ELEMENT_MAX) - 1);
1219 void
1220 gnm_style_unset_element (GnmStyle *style, GnmStyleElement elem)
1222 g_return_if_fail (style != NULL);
1223 g_return_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX);
1225 if (elem_is_set (style, elem)) {
1226 elem_clear_contents (style, elem);
1227 elem_unset (style, elem);
1232 * gnm_style_merge :
1233 * @base: #GnmStyle
1234 * @overlay: #GnmStyle
1236 * Applies all active elements of @overlay onto @base.
1238 void
1239 gnm_style_merge (GnmStyle *base, GnmStyle const *overlay)
1241 unsigned i;
1242 if (base == overlay)
1243 return;
1244 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
1245 if (elem_is_set (overlay, i)) {
1246 elem_clear_contents (base, i);
1247 elem_assign_contents (base, overlay, i);
1248 elem_changed (base, i);
1253 * gnm_style_merge_element:
1254 * @dst: Destination style
1255 * @src: Source style
1256 * @elem: Element to replace
1258 * This function replaces element @elem in style @dst with element @elem
1259 * in style @src. (If element @elem was already set in style @dst then
1260 * the element will first be unset)
1262 void
1263 gnm_style_merge_element (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
1265 g_return_if_fail (src != NULL);
1266 g_return_if_fail (dst != NULL);
1267 g_return_if_fail (src != dst);
1269 if (elem_is_set (src, elem)) {
1270 elem_clear_contents (dst, elem);
1271 elem_assign_contents (dst, src, elem);
1272 elem_set (dst, elem);
1273 elem_changed (dst, elem);
1277 void
1278 gnm_style_set_font_color (GnmStyle *style, GnmColor *col)
1280 g_return_if_fail (style != NULL);
1281 g_return_if_fail (col != NULL);
1283 elem_changed (style, MSTYLE_FONT_COLOR);
1284 if (elem_is_set (style, MSTYLE_FONT_COLOR))
1285 style_color_unref (style->color.font);
1286 else
1287 elem_set (style, MSTYLE_FONT_COLOR);
1288 elem_changed (style, MSTYLE_FONT_COLOR);
1289 style->color.font = col;
1290 gnm_style_clear_pango (style);
1294 * gnm_style_set_back_color :
1295 * @style: #GnmStyle
1296 * @col: #GnmColor
1298 * Assigns @col as the background of @style.
1300 * NOTE : the background colour is only visibile if
1301 * GnmStyle::pattern > 0
1303 void
1304 gnm_style_set_back_color (GnmStyle *style, GnmColor *col)
1306 g_return_if_fail (style != NULL);
1307 g_return_if_fail (col != NULL);
1309 elem_changed (style, MSTYLE_COLOR_BACK);
1310 if (elem_is_set (style, MSTYLE_COLOR_BACK))
1311 style_color_unref (style->color.back);
1312 else
1313 elem_set (style, MSTYLE_COLOR_BACK);
1314 style->color.back = col;
1315 gnm_style_clear_pango (style);
1317 void
1318 gnm_style_set_pattern_color (GnmStyle *style, GnmColor *col)
1320 g_return_if_fail (style != NULL);
1321 g_return_if_fail (col != NULL);
1323 elem_changed (style, MSTYLE_COLOR_PATTERN);
1324 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1325 style_color_unref (style->color.pattern);
1326 else
1327 elem_set (style, MSTYLE_COLOR_PATTERN);
1328 style->color.pattern = col;
1329 gnm_style_clear_pango (style);
1332 GnmColor *
1333 gnm_style_get_font_color (GnmStyle const *style)
1335 g_return_val_if_fail (style != NULL, NULL);
1336 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_COLOR), NULL);
1337 return style->color.font;
1340 GnmColor *
1341 gnm_style_get_back_color (GnmStyle const *style)
1343 g_return_val_if_fail (style != NULL, NULL);
1344 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_BACK), NULL);
1345 return style->color.back;
1348 GnmColor *
1349 gnm_style_get_pattern_color (GnmStyle const *style)
1351 g_return_val_if_fail (style != NULL, NULL);
1352 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_PATTERN), NULL);
1353 return style->color.pattern;
1356 void
1357 gnm_style_set_border (GnmStyle *style, GnmStyleElement elem,
1358 GnmBorder *border)
1360 g_return_if_fail (style != NULL);
1362 /* NOTE : It is legal for border to be NULL */
1363 switch (elem) {
1364 case MSTYLE_ANY_BORDER:
1365 elem_changed (style, elem);
1366 elem_set (style, elem);
1367 elem -= MSTYLE_BORDER_TOP;
1368 if (style->borders[elem])
1369 gnm_style_border_unref (style->borders[elem]);
1370 style->borders[elem] = border;
1371 break;
1372 default:
1373 g_warning ("Not a border element");
1374 break;
1378 GnmBorder *
1379 gnm_style_get_border (GnmStyle const *style, GnmStyleElement elem)
1381 g_return_val_if_fail (style != NULL, NULL);
1383 switch (elem) {
1384 case MSTYLE_ANY_BORDER:
1385 return style->borders[elem - MSTYLE_BORDER_TOP ];
1387 default:
1388 g_warning ("Not a border element");
1389 return NULL;
1393 void
1394 gnm_style_set_pattern (GnmStyle *style, int pattern)
1396 g_return_if_fail (style != NULL);
1397 g_return_if_fail (pattern >= 0);
1398 g_return_if_fail (pattern < GNM_PATTERNS_MAX);
1400 elem_changed (style, MSTYLE_PATTERN);
1401 elem_set (style, MSTYLE_PATTERN);
1402 style->pattern = pattern;
1406 gnm_style_get_pattern (GnmStyle const *style)
1408 g_return_val_if_fail (style != NULL, 0);
1409 g_return_val_if_fail (elem_is_set (style, MSTYLE_PATTERN), 0);
1411 return style->pattern;
1415 * gnm_style_get_font:
1416 * @style: #GnmStyle
1417 * @context: #PangoContext
1419 * Returns: (transfer none):
1421 GnmFont *
1422 gnm_style_get_font (GnmStyle const *style, PangoContext *context)
1424 g_return_val_if_fail (style != NULL, NULL);
1426 if (!style->font || style->font_context != context) {
1427 char const *name;
1428 gboolean bold, italic;
1429 double size;
1431 gnm_style_clear_font ((GnmStyle *)style);
1433 if (elem_is_set (style, MSTYLE_FONT_NAME))
1434 name = gnm_style_get_font_name (style);
1435 else
1436 name = DEFAULT_FONT;
1438 if (elem_is_set (style, MSTYLE_FONT_BOLD))
1439 bold = gnm_style_get_font_bold (style);
1440 else
1441 bold = FALSE;
1443 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
1444 italic = gnm_style_get_font_italic (style);
1445 else
1446 italic = FALSE;
1448 if (elem_is_set (style, MSTYLE_FONT_SIZE))
1449 size = gnm_style_get_font_size (style);
1450 else
1451 size = DEFAULT_SIZE;
1453 ((GnmStyle *)style)->font =
1454 gnm_font_new (context, name, size, bold, italic);
1455 ((GnmStyle *)style)->font_context = g_object_ref (context);
1458 return style->font;
1461 void
1462 gnm_style_set_font_name (GnmStyle *style, char const *name)
1464 g_return_if_fail (name != NULL);
1465 g_return_if_fail (style != NULL);
1467 elem_changed (style, MSTYLE_FONT_NAME);
1468 if (elem_is_set (style, MSTYLE_FONT_NAME))
1469 go_string_unref (style->font_detail.name);
1470 else
1471 elem_set (style, MSTYLE_FONT_NAME);
1472 style->font_detail.name = go_string_new (name);
1473 gnm_style_clear_font (style);
1474 gnm_style_clear_pango (style);
1477 char const *
1478 gnm_style_get_font_name (GnmStyle const *style)
1480 g_return_val_if_fail (style != NULL, NULL);
1481 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_NAME), NULL);
1483 return style->font_detail.name->str;
1486 void
1487 gnm_style_set_font_bold (GnmStyle *style, gboolean bold)
1489 g_return_if_fail (style != NULL);
1491 elem_changed (style, MSTYLE_FONT_BOLD);
1492 elem_set (style, MSTYLE_FONT_BOLD);
1493 style->font_detail.bold = !!bold;
1494 gnm_style_clear_font (style);
1495 gnm_style_clear_pango (style);
1498 gboolean
1499 gnm_style_get_font_bold (GnmStyle const *style)
1501 g_return_val_if_fail (style != NULL, FALSE);
1502 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_BOLD), FALSE);
1504 return style->font_detail.bold;
1507 void
1508 gnm_style_set_font_italic (GnmStyle *style, gboolean italic)
1510 g_return_if_fail (style != NULL);
1512 elem_changed (style, MSTYLE_FONT_ITALIC);
1513 elem_set (style, MSTYLE_FONT_ITALIC);
1514 style->font_detail.italic = !!italic;
1515 gnm_style_clear_font (style);
1516 gnm_style_clear_pango (style);
1519 gboolean
1520 gnm_style_get_font_italic (GnmStyle const *style)
1522 g_return_val_if_fail (style != NULL, FALSE);
1523 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_ITALIC), FALSE);
1525 return style->font_detail.italic;
1528 void
1529 gnm_style_set_font_uline (GnmStyle *style, GnmUnderline const underline)
1531 g_return_if_fail (style != NULL);
1532 g_return_if_fail (underline >= UNDERLINE_NONE && underline <= UNDERLINE_DOUBLE_LOW);
1534 elem_changed (style, MSTYLE_FONT_UNDERLINE);
1535 elem_set (style, MSTYLE_FONT_UNDERLINE);
1536 style->font_detail.underline = underline;
1537 gnm_style_clear_pango (style);
1540 GnmUnderline
1541 gnm_style_get_font_uline (GnmStyle const *style)
1543 g_return_val_if_fail (style != NULL, UNDERLINE_NONE);
1544 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_UNDERLINE), UNDERLINE_NONE);
1546 return style->font_detail.underline;
1549 void
1550 gnm_style_set_font_strike (GnmStyle *style, gboolean strikethrough)
1552 g_return_if_fail (style != NULL);
1554 elem_changed (style, MSTYLE_FONT_STRIKETHROUGH);
1555 elem_set (style, MSTYLE_FONT_STRIKETHROUGH);
1556 style->font_detail.strikethrough = !!strikethrough;
1557 gnm_style_clear_pango (style);
1560 gboolean
1561 gnm_style_get_font_strike (GnmStyle const *style)
1563 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
1565 return style->font_detail.strikethrough;
1568 void
1569 gnm_style_set_font_script (GnmStyle *style, GOFontScript script)
1571 g_return_if_fail (style != NULL);
1572 elem_changed (style, MSTYLE_FONT_SCRIPT);
1573 elem_set (style, MSTYLE_FONT_SCRIPT);
1574 style->font_detail.script = script;
1575 gnm_style_clear_pango (style);
1578 GOFontScript
1579 gnm_style_get_font_script (GnmStyle const *style)
1581 g_return_val_if_fail (style != NULL, GO_FONT_SCRIPT_STANDARD);
1582 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SCRIPT), GO_FONT_SCRIPT_STANDARD);
1584 return style->font_detail.script;
1587 void
1588 gnm_style_set_font_size (GnmStyle *style, double size)
1590 g_return_if_fail (style != NULL);
1591 g_return_if_fail (size >= 1.);
1592 elem_changed (style, MSTYLE_FONT_SIZE);
1593 elem_set (style, MSTYLE_FONT_SIZE);
1594 style->font_detail.size = size;
1595 gnm_style_clear_font (style);
1596 gnm_style_clear_pango (style);
1599 double
1600 gnm_style_get_font_size (GnmStyle const *style)
1602 g_return_val_if_fail (style != NULL, 12.0);
1603 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SIZE), 12.0);
1605 return style->font_detail.size;
1608 void
1609 gnm_style_set_format (GnmStyle *style, GOFormat const *format)
1611 g_return_if_fail (style != NULL);
1612 g_return_if_fail (format != NULL);
1614 elem_changed (style, MSTYLE_FORMAT);
1615 go_format_ref (format);
1616 elem_clear_contents (style, MSTYLE_FORMAT);
1617 elem_set (style, MSTYLE_FORMAT);
1618 style->format = format;
1622 * gnm_style_set_format_text:
1624 * @style: mstyle to change.
1625 * @format: An *untranslated* format string.
1627 void
1628 gnm_style_set_format_text (GnmStyle *style, char const *format)
1630 GOFormat *sf;
1632 g_return_if_fail (style != NULL);
1633 g_return_if_fail (format != NULL);
1635 sf = go_format_new_from_XL (format);
1636 gnm_style_set_format (style, sf);
1637 go_format_unref (sf);
1640 const GOFormat *
1641 gnm_style_get_format (GnmStyle const *style)
1643 g_return_val_if_fail (style != NULL, NULL);
1644 g_return_val_if_fail (elem_is_set (style, MSTYLE_FORMAT), NULL);
1646 return style->format;
1649 void
1650 gnm_style_set_align_h (GnmStyle *style, GnmHAlign a)
1652 g_return_if_fail (style != NULL);
1654 elem_changed (style, MSTYLE_ALIGN_H);
1655 elem_set (style, MSTYLE_ALIGN_H);
1656 style->h_align = a;
1659 GnmHAlign
1660 gnm_style_get_align_h (GnmStyle const *style)
1662 g_return_val_if_fail (style != NULL, GNM_HALIGN_LEFT);
1663 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), GNM_HALIGN_LEFT);
1665 return style->h_align;
1668 void
1669 gnm_style_set_align_v (GnmStyle *style, GnmVAlign a)
1671 g_return_if_fail (style != NULL);
1673 elem_changed (style, MSTYLE_ALIGN_V);
1674 elem_set (style, MSTYLE_ALIGN_V);
1675 style->v_align = a;
1678 GnmVAlign
1679 gnm_style_get_align_v (GnmStyle const *style)
1681 g_return_val_if_fail (style != NULL, GNM_VALIGN_TOP);
1682 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), GNM_VALIGN_TOP);
1684 return style->v_align;
1687 void
1688 gnm_style_set_indent (GnmStyle *style, int i)
1690 g_return_if_fail (style != NULL);
1692 elem_changed (style, MSTYLE_INDENT);
1693 elem_set (style, MSTYLE_INDENT);
1694 style->indent = i;
1698 gnm_style_get_indent (GnmStyle const *style)
1700 g_return_val_if_fail (style != NULL, 0);
1701 g_return_val_if_fail (elem_is_set (style, MSTYLE_INDENT), 0);
1703 return style->indent;
1706 void
1707 gnm_style_set_rotation (GnmStyle *style, int rot_deg)
1709 g_return_if_fail (style != NULL);
1711 elem_changed (style, MSTYLE_ROTATION);
1712 elem_set (style, MSTYLE_ROTATION);
1713 style->rotation = rot_deg;
1717 gnm_style_get_rotation (GnmStyle const *style)
1719 g_return_val_if_fail (style != NULL, 0);
1720 g_return_val_if_fail (elem_is_set (style, MSTYLE_ROTATION), 0);
1722 return style->rotation;
1725 void
1726 gnm_style_set_text_dir (GnmStyle *style, GnmTextDir text_dir)
1728 g_return_if_fail (style != NULL);
1730 elem_changed (style, MSTYLE_TEXT_DIR);
1731 elem_set (style, MSTYLE_TEXT_DIR);
1732 style->text_dir = text_dir;
1735 GnmTextDir
1736 gnm_style_get_text_dir (GnmStyle const *style)
1738 g_return_val_if_fail (style != NULL, GNM_TEXT_DIR_CONTEXT);
1739 g_return_val_if_fail (elem_is_set (style, MSTYLE_TEXT_DIR), GNM_TEXT_DIR_CONTEXT);
1741 return style->text_dir;
1744 void
1745 gnm_style_set_wrap_text (GnmStyle *style, gboolean f)
1747 g_return_if_fail (style != NULL);
1749 elem_changed (style, MSTYLE_WRAP_TEXT);
1750 elem_set (style, MSTYLE_WRAP_TEXT);
1751 style->wrap_text = !!f;
1754 gboolean
1755 gnm_style_get_wrap_text (GnmStyle const *style)
1757 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1759 return style->wrap_text;
1763 * Same as gnm_style_get_wrap_text except that if either halign or valign
1764 * is _JUSTIFY, the result will be TRUE.
1766 gboolean
1767 gnm_style_get_effective_wrap_text (GnmStyle const *style)
1769 g_return_val_if_fail (style != NULL, FALSE);
1770 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1771 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), FALSE);
1772 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), FALSE);
1774 /* Note: GNM_HALIGN_GENERAL never expands to GNM_HALIGN_JUSTIFY. */
1775 return (style->wrap_text ||
1776 style->v_align == GNM_VALIGN_JUSTIFY ||
1777 style->v_align == GNM_VALIGN_DISTRIBUTED ||
1778 style->h_align == GNM_HALIGN_JUSTIFY);
1781 void
1782 gnm_style_set_shrink_to_fit (GnmStyle *style, gboolean f)
1784 g_return_if_fail (style != NULL);
1786 elem_changed (style, MSTYLE_SHRINK_TO_FIT);
1787 elem_set (style, MSTYLE_SHRINK_TO_FIT);
1788 style->shrink_to_fit = !!f;
1791 gboolean
1792 gnm_style_get_shrink_to_fit (GnmStyle const *style)
1794 g_return_val_if_fail (style != NULL, FALSE);
1795 g_return_val_if_fail (elem_is_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
1797 return style->shrink_to_fit;
1800 void
1801 gnm_style_set_contents_locked (GnmStyle *style, gboolean f)
1803 g_return_if_fail (style != NULL);
1805 elem_changed (style, MSTYLE_CONTENTS_LOCKED);
1806 elem_set (style, MSTYLE_CONTENTS_LOCKED);
1807 style->contents_locked = !!f;
1810 gboolean
1811 gnm_style_get_contents_locked (GnmStyle const *style)
1813 g_return_val_if_fail (style != NULL, FALSE);
1814 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_LOCKED), FALSE);
1816 return style->contents_locked;
1819 void
1820 gnm_style_set_contents_hidden (GnmStyle *style, gboolean f)
1822 g_return_if_fail (style != NULL);
1824 elem_changed (style, MSTYLE_CONTENTS_HIDDEN);
1825 elem_set (style, MSTYLE_CONTENTS_HIDDEN);
1826 style->contents_hidden = !!f;
1829 gboolean
1830 gnm_style_get_contents_hidden (GnmStyle const *style)
1832 g_return_val_if_fail (style != NULL, FALSE);
1833 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN), FALSE);
1835 return style->contents_hidden;
1839 * gnm_style_set_validation:
1840 * @style: #GnmStyle
1841 * @v: (transfer full): #GnmValidation
1843 void
1844 gnm_style_set_validation (GnmStyle *style, GnmValidation *v)
1846 g_return_if_fail (style != NULL);
1848 elem_clear_contents (style, MSTYLE_VALIDATION);
1849 elem_changed (style, MSTYLE_VALIDATION);
1850 elem_set (style, MSTYLE_VALIDATION);
1851 style->validation = v;
1855 * gnm_style_get_validation:
1856 * @style: #GnmStyle
1858 * Returns: (transfer none):
1860 GnmValidation const *
1861 gnm_style_get_validation (GnmStyle const *style)
1863 g_return_val_if_fail (style != NULL, NULL);
1864 g_return_val_if_fail (elem_is_set (style, MSTYLE_VALIDATION), NULL);
1866 return style->validation;
1870 * gnm_style_set_hlink:
1871 * @style: #GnmStyle
1872 * @lnk: (transfer full): #GnmHLink
1874 * This sets a link for @style.
1876 void
1877 gnm_style_set_hlink (GnmStyle *style, GnmHLink *lnk)
1879 g_return_if_fail (style != NULL);
1881 elem_clear_contents (style, MSTYLE_HLINK);
1882 elem_changed (style, MSTYLE_HLINK);
1883 elem_set (style, MSTYLE_HLINK);
1884 style->hlink = lnk;
1888 * gnm_style_get_hlink:
1889 * @style: #GnmStyle
1891 * Returns: (transfer none): the associated #GnmHLink.
1893 GnmHLink *
1894 gnm_style_get_hlink (GnmStyle const *style)
1896 g_return_val_if_fail (style != NULL, NULL);
1897 g_return_val_if_fail (elem_is_set (style, MSTYLE_HLINK), NULL);
1899 return style->hlink;
1903 * gnm_style_set_input_msg:
1904 * @style: #GnmStyle
1905 * @msg: (transfer full): #GnmInputMsg
1907 * This sets an input message for @style.
1909 void
1910 gnm_style_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
1912 g_return_if_fail (style != NULL);
1914 elem_clear_contents (style, MSTYLE_INPUT_MSG);
1915 elem_changed (style, MSTYLE_INPUT_MSG);
1916 elem_set (style, MSTYLE_INPUT_MSG);
1917 style->input_msg = msg;
1921 * gnm_style_get_input_msg:
1922 * @style: #GnmStyle
1924 * Returns: (transfer none): the currently set input message assuming
1925 * that the style has such.
1927 GnmInputMsg *
1928 gnm_style_get_input_msg (GnmStyle const *style)
1930 g_return_val_if_fail (style != NULL, NULL);
1931 g_return_val_if_fail (elem_is_set (style, MSTYLE_INPUT_MSG), NULL);
1933 return style->input_msg;
1937 * gnm_style_set_conditions:
1938 * @style: #GnmStyle
1939 * @sc: (transfer full): #GnmStyleConditions
1941 * This sets conditional style for @style.
1943 void
1944 gnm_style_set_conditions (GnmStyle *style, GnmStyleConditions *sc)
1946 g_return_if_fail (style != NULL);
1948 elem_clear_contents (style, MSTYLE_CONDITIONS);
1949 elem_changed (style, MSTYLE_CONDITIONS);
1950 elem_set (style, MSTYLE_CONDITIONS);
1951 style->conditions = sc;
1955 * gnm_style_get_conditions:
1956 * @style: #GnmStyle
1958 * Returns: (transfer none): the currently set conditional style assuming
1959 * that the style has such.
1961 GnmStyleConditions *
1962 gnm_style_get_conditions (GnmStyle const *style)
1964 g_return_val_if_fail (style != NULL, NULL);
1965 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
1966 return style->conditions;
1969 static gboolean
1970 debug_style_deps (void)
1972 static int debug = -1;
1973 if (debug < 0)
1974 debug = gnm_debug_flag ("style-deps");
1975 return debug;
1979 * Just a simple version for now. We can also ignore most function
1980 * calls[1] and self-references[2].
1982 * [1] Excluding volatile (TODAY, ...) and those that can create references
1983 * outside the arguments (INDIRECT).
1985 * [2] References that print like A1 when used in A1.
1987 static gboolean
1988 cond_expr_harmless (GnmExpr const *expr)
1990 GnmValue const *v = gnm_expr_get_constant (expr);
1991 if (v && !VALUE_IS_CELLRANGE (v))
1992 return TRUE;
1994 return FALSE;
1998 void
1999 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
2001 GnmStyleConditions *sc;
2002 Sheet *sheet;
2004 g_return_if_fail (style != NULL);
2005 g_return_if_fail (r != NULL);
2007 sheet = style->linked_sheet;
2010 * Conditional formatting.
2012 * We need to trigger a reformatting of the cell if a cell referenced
2013 * by the condition changes.
2015 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2016 ? gnm_style_get_conditions (style)
2017 : NULL;
2018 if (sc) {
2019 GPtrArray const *conds = gnm_style_conditions_details (sc);
2020 guint ui;
2021 if (debug_style_deps ())
2022 g_printerr ("Linking %s for %p\n",
2023 range_as_string (r), style);
2024 for (ui = 0; conds && ui < conds->len; ui++) {
2025 GnmStyleCond const *c = g_ptr_array_index (conds, ui);
2026 guint ei;
2028 for (ei = 0; ei < 2; ei++) {
2029 GnmExprTop const *texpr =
2030 gnm_style_cond_get_expr (c, ei);
2031 if (!texpr ||
2032 cond_expr_harmless (texpr->expr))
2033 continue;
2034 if (!style->deps)
2035 style->deps = g_ptr_array_new ();
2036 gnm_dep_style_dependency
2037 (sheet, texpr, r, style->deps);
2043 * Validations.
2045 * We can probably ignore those. If a dependent cell changes such
2046 * that a validation condition is no longer satisfied, it is
2047 * grandfathered in as valid.
2050 /* The style owns the deps. */
2053 void
2054 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2056 unsigned ui, k;
2058 g_return_if_fail (style != NULL);
2059 g_return_if_fail (r != NULL);
2061 if (!style->deps)
2062 return;
2064 for (ui = k = 0; ui < style->deps->len; ui++) {
2065 GnmDependent *dep = g_ptr_array_index (style->deps, ui);
2066 GnmCellPos const *pos = dependent_pos (dep);
2068 if (range_contains (r, pos->col, pos->row)) {
2069 if (debug_style_deps ())
2070 g_printerr ("Unlinking %s for %p\n",
2071 cellpos_as_string (pos), style);
2072 dependent_set_expr (dep, NULL);
2073 g_free (dep);
2074 } else {
2075 g_ptr_array_index (style->deps, k) = dep;
2076 k++;
2080 g_ptr_array_set_size (style->deps, k);
2085 * gnm_style_visible_in_blank:
2086 * @style: style to query
2088 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2089 * that means if it has a background or a visible border.
2091 gboolean
2092 gnm_style_visible_in_blank (GnmStyle const *style)
2094 GnmStyleElement i;
2096 g_return_val_if_fail (style != NULL, FALSE);
2098 if (elem_is_set (style, MSTYLE_PATTERN) &&
2099 gnm_style_get_pattern (style) > 0)
2100 return TRUE;
2102 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2103 if (elem_is_set (style, i) &&
2104 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2105 return TRUE;
2107 return FALSE;
2110 static void
2111 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2113 attr->start_index = 0;
2114 attr->end_index = G_MAXINT;
2115 pango_attr_list_insert (attrs, attr);
2119 * gnm_style_get_pango_attrs :
2120 * @style: #GnmStyle
2122 PangoAttrList *
2123 gnm_style_get_pango_attrs (GnmStyle const *style,
2124 PangoContext *context,
2125 double zoom)
2127 PangoAttrList *l;
2128 GnmUnderline ul;
2129 GnmFont *font = gnm_style_get_font (style, context);
2131 if (style->pango_attrs) {
2132 if (zoom == style->pango_attrs_zoom) {
2133 pango_attr_list_ref (style->pango_attrs);
2134 return style->pango_attrs;
2136 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2139 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2140 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2141 ((GnmStyle *)style)->pango_attrs_height = -1;
2143 /* Foreground colour. */
2144 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2145 if (0) {
2146 GnmColor const *fore = style->color.font;
2147 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2150 /* Handle underlining. */
2151 ul = gnm_style_get_font_uline (style);
2152 if (ul != UNDERLINE_NONE)
2153 add_attr (l,
2154 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2156 /* Handle strikethrough. */
2157 if (gnm_style_get_font_strike (style))
2158 add_attr (l, pango_attr_strikethrough_new (TRUE));
2160 /* Handle script. */
2161 switch (gnm_style_get_font_script (style)) {
2162 default :
2163 case GO_FONT_SCRIPT_STANDARD :
2164 break;
2165 case GO_FONT_SCRIPT_SUB :
2166 add_attr (l, go_pango_attr_subscript_new (TRUE));
2167 break;
2168 case GO_FONT_SCRIPT_SUPER :
2169 add_attr (l, go_pango_attr_superscript_new (TRUE));
2170 break;
2173 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2175 if (zoom != 1)
2176 add_attr (l, pango_attr_scale_new (zoom));
2178 pango_attr_list_ref (l);
2179 return l;
2182 PangoAttrList *
2183 gnm_style_generate_attrs_full (GnmStyle const *style)
2185 GnmColor const *fore = style->color.font;
2186 PangoAttrList *l = pango_attr_list_new ();
2188 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2189 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2190 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2191 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2192 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2193 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2194 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2195 add_attr (l, pango_attr_strikethrough_new
2196 (gnm_style_get_font_strike (style)));
2197 add_attr (l, pango_attr_underline_new
2198 (gnm_translate_underline_to_pango
2199 (gnm_style_get_font_uline (style))));
2200 return l;
2204 gnm_style_get_pango_height (GnmStyle const *style,
2205 PangoContext *context,
2206 double zoom)
2208 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2210 if (style->pango_attrs_height == -1) {
2211 int h;
2212 PangoLayout *layout = pango_layout_new (context);
2213 GOFormat const *fmt;
2214 gboolean requires_translation = FALSE;
2216 fmt = gnm_style_get_format (style);
2217 if (!go_format_is_general (fmt)) {
2218 GOFormatDetails details;
2219 go_format_get_details (fmt, &details, NULL);
2220 if (details.family == GO_FORMAT_SCIENTIFIC &&
2221 details.use_markup) {
2222 PangoAttribute *a
2223 = go_pango_attr_superscript_new (TRUE);
2224 /* We want to superscript the "-01" in the */
2225 /* string "+1.23456789E-01" */
2226 a->start_index = 12;
2227 a->end_index = 15;
2228 pango_attr_list_insert (attrs, a);
2229 requires_translation = TRUE;
2232 pango_layout_set_attributes (layout, attrs);
2233 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2234 if (requires_translation)
2235 go_pango_translate_layout (layout);
2236 pango_layout_get_pixel_size (layout, NULL, &h);
2237 g_object_unref (layout);
2238 ((GnmStyle *)style)->pango_attrs_height = h;
2241 pango_attr_list_unref (attrs);
2242 return style->pango_attrs_height;
2246 void
2247 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2249 switch (attr->klass->type) {
2250 case PANGO_ATTR_FAMILY :
2251 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2252 break;
2253 case PANGO_ATTR_SIZE :
2254 gnm_style_set_font_size (style,
2255 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2256 break;
2257 case PANGO_ATTR_STYLE :
2258 gnm_style_set_font_italic (style,
2259 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2260 break;
2261 case PANGO_ATTR_WEIGHT :
2262 gnm_style_set_font_bold (style,
2263 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2264 break;
2265 case PANGO_ATTR_FOREGROUND :
2266 gnm_style_set_font_color (style, gnm_color_new_pango (
2267 &((PangoAttrColor *)attr)->color));
2268 break;
2269 case PANGO_ATTR_UNDERLINE :
2270 gnm_style_set_font_uline
2271 (style, gnm_translate_underline_from_pango
2272 (((PangoAttrInt *)attr)->value));
2273 break;
2274 case PANGO_ATTR_STRIKETHROUGH :
2275 gnm_style_set_font_strike (style,
2276 ((PangoAttrInt *)attr)->value != 0);
2277 break;
2278 default : {
2279 gboolean script_seen = FALSE, script_set = FALSE;
2280 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2281 script_seen = TRUE;
2282 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2283 script_set = TRUE;
2284 gnm_style_set_font_script
2285 (style, GO_FONT_SCRIPT_SUPER);
2287 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2288 script_seen = TRUE;
2289 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2290 script_set = TRUE;
2291 gnm_style_set_font_script
2292 (style, GO_FONT_SCRIPT_SUB);
2295 if (script_seen && !script_set)
2296 gnm_style_set_font_script
2297 (style, GO_FONT_SCRIPT_STANDARD);
2298 break; /* ignored */
2303 /* ------------------------------------------------------------------------- */
2305 static void
2306 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2308 if (color)
2309 g_printerr ("\t%s: %x:%x:%x%s\n",
2310 gnm_style_element_name [elem],
2311 GO_COLOR_UINT_R (color->go_color),
2312 GO_COLOR_UINT_G (color->go_color),
2313 GO_COLOR_UINT_B (color->go_color),
2314 color->is_auto ? " auto" : "");
2315 else
2316 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2319 static void
2320 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2322 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2323 if (border)
2324 g_printerr ("%d\n", border->line_type);
2325 else
2326 g_printerr ("blank\n");
2330 * gnm_style_dump:
2331 * @style: style to dump
2333 * This function dumps the given style's contents to stderr. This is meant
2334 * for debug purposes only and doesn't do a very good job for, for example,
2335 * conditional style settings.
2337 void
2338 gnm_style_dump (GnmStyle const *style)
2340 int i;
2342 g_printerr ("Style Refs %d\n", style->ref_count);
2343 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2344 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2345 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2346 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2348 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2349 if (elem_is_set (style, i))
2350 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2352 if (elem_is_set (style, MSTYLE_PATTERN))
2353 g_printerr ("\tpattern %d\n", style->pattern);
2354 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2355 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2356 if (elem_is_set (style, MSTYLE_FONT_NAME))
2357 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2358 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2359 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2360 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2361 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2362 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2363 switch (style->font_detail.underline) {
2364 default :
2365 case UNDERLINE_NONE :
2366 g_printerr ("\tno underline\n"); break;
2367 case UNDERLINE_SINGLE :
2368 g_printerr ("\tsingle underline\n"); break;
2369 case UNDERLINE_DOUBLE :
2370 g_printerr ("\tdouble underline\n"); break;
2372 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2373 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2374 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2375 switch (style->font_detail.script) {
2376 case GO_FONT_SCRIPT_SUB :
2377 g_printerr ("\tsubscript\n"); break;
2378 default :
2379 case GO_FONT_SCRIPT_STANDARD :
2380 g_printerr ("\tno super or sub\n"); break;
2381 case GO_FONT_SCRIPT_SUPER :
2382 g_printerr ("\tsuperscript\n"); break;
2384 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2385 g_printerr ("\tsize %f\n", style->font_detail.size);
2386 if (elem_is_set (style, MSTYLE_FORMAT)) {
2387 const char *fmt = go_format_as_XL (style->format);
2388 g_printerr ("\tformat '%s'\n", fmt);
2390 if (elem_is_set (style, MSTYLE_ALIGN_V))
2391 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2392 if (elem_is_set (style, MSTYLE_ALIGN_H))
2393 g_printerr ("\thalign %hd\n", (short)style->h_align);
2394 if (elem_is_set (style, MSTYLE_INDENT))
2395 g_printerr ("\tindent %d\n", style->indent);
2396 if (elem_is_set (style, MSTYLE_ROTATION))
2397 g_printerr ("\trotation %d\n", style->rotation);
2398 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2399 g_printerr ("\ttext dir %d\n", style->text_dir);
2400 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2401 g_printerr ("\twrap text %d\n", style->wrap_text);
2402 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2403 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2404 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2405 g_printerr ("\tlocked %d\n", style->contents_locked);
2406 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2407 g_printerr ("\thidden %d\n", style->contents_hidden);
2408 if (elem_is_set (style, MSTYLE_VALIDATION))
2409 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2410 if (elem_is_set (style, MSTYLE_HLINK))
2411 g_printerr ("\thlink %p\n", (void *)style->hlink);
2412 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2413 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2414 if (elem_is_set (style, MSTYLE_CONDITIONS))
2415 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2418 /* ------------------------------------------------------------------------- */
2420 void
2421 gnm_style_init (void)
2423 #if USE_MSTYLE_POOL
2424 gnm_style_pool =
2425 go_mem_chunk_new ("style pool",
2426 sizeof (GnmStyle),
2427 16 * 1024 - 128);
2428 #endif
2431 #if USE_MSTYLE_POOL
2432 static void
2433 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2435 GnmStyle *style = data;
2436 g_printerr ("Leaking style at %p.\n", (void *)style);
2437 gnm_style_dump (style);
2439 #endif
2441 void
2442 gnm_style_shutdown (void)
2444 #if USE_MSTYLE_POOL
2445 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2446 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2447 gnm_style_pool = NULL;
2448 #endif