ssdiff: fix handling of hyperlinks.
[gnumeric.git] / src / mstyle.c
blob1ca5455f4880a7011347200810cbb6caf8469cab
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 "application.h"
20 #include "parse-util.h"
21 #include "expr.h"
22 #include "value.h"
23 #include "gutils.h"
24 #include "ranges.h"
25 #include "gnumeric-conf.h"
26 #include <goffice/goffice.h>
27 #include <string.h>
29 #define DEBUG_STYLES
30 #ifndef USE_MSTYLE_POOL
31 #define USE_MSTYLE_POOL 1
32 #endif
34 #if USE_MSTYLE_POOL
35 /* Memory pool for GnmStyles. */
36 static GOMemChunk *gnm_style_pool;
37 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
38 #define CHUNK_ALLOC0(T,p) ((T*)go_mem_chunk_alloc0 (p))
39 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
40 #else
41 #define CHUNK_ALLOC(T,c) g_new (T,1)
42 #define CHUNK_ALLOC0(T,c) g_new0 (T,1)
43 #define CHUNK_FREE(p,v) g_free ((v))
44 #endif
46 #define UNROLLED_FOR(init_,cond_,step_,code_) \
47 do { \
48 init_; \
49 if (cond_) { code_; step_; \
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 g_assert_not_reached (); \
100 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} \
101 } while (0)
105 static char const * const
106 gnm_style_element_name[MSTYLE_ELEMENT_MAX] = {
107 "Color.Back",
108 "Color.Pattern",
109 "Border.Top",
110 "Border.Bottom",
111 "Border.Left",
112 "Border.Right",
113 "Border.RevDiagonal",
114 "Border.Diagonal",
115 "Pattern",
116 "Color.Fore",
117 "Font.Name",
118 "Font.Bold",
119 "Font.Italic",
120 "Font.Underline",
121 "Font.Strikethrough",
122 "Font.Script",
123 "Font.Size",
124 "Format",
125 "Align.v",
126 "Align.h",
127 "Indent",
128 "Rotation",
129 "WrapText",
130 "ShrinkToFit",
131 "Contents.Locked",
132 "Contents.Hidden",
133 "Validation",
134 "Hyper Link",
135 "Input Msg"
138 /* Some ref/link count debugging */
139 #if 0
140 #define d(arg) g_printerr arg
141 #else
142 #define d(arg) do { } while (0)
143 #endif
145 static void
146 clear_conditional_merges (GnmStyle *style)
148 if (style->cond_styles) {
149 unsigned i = style->cond_styles->len;
150 while (i-- > 0)
151 gnm_style_unref (g_ptr_array_index (style->cond_styles, i));
152 g_ptr_array_free (style->cond_styles, TRUE);
153 style->cond_styles = NULL;
157 #define MIX(H) do { \
158 H *= G_GUINT64_CONSTANT(123456789012345); \
159 H ^= (H >> 31); \
160 } while (0)
162 static void
163 gnm_style_update (GnmStyle *style)
165 guint64 hash = 0;
166 int i;
168 g_return_if_fail (style->changed);
170 style->changed = 0;
172 clear_conditional_merges (style);
173 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
174 style->cond_styles = gnm_style_conditions_overlay (style->conditions, style);
176 /* ---------------------------------------- */
178 if (elem_is_set (style, MSTYLE_COLOR_BACK)) {
179 if (!style->color.back->is_auto)
180 hash ^= GPOINTER_TO_UINT (style->color.back);
181 else
182 hash++;
184 MIX (hash);
186 if (elem_is_set (style, MSTYLE_COLOR_PATTERN)) {
187 if (!style->color.pattern->is_auto)
188 hash ^= GPOINTER_TO_UINT (style->color.pattern);
189 else
190 hash++;
192 MIX (hash);
194 if (elem_is_set (style, MSTYLE_FONT_COLOR)) {
195 if (!style->color.font->is_auto)
196 hash ^= GPOINTER_TO_UINT (style->color.font);
197 else
198 hash++;
200 MIX (hash);
202 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
203 if (elem_is_set (style, i))
204 hash ^= GPOINTER_TO_UINT (style->borders[i - MSTYLE_BORDER_TOP]);
205 else
206 hash++;
207 MIX (hash);
210 if (elem_is_set (style, MSTYLE_PATTERN))
211 hash ^= style->pattern;
212 MIX (hash);
214 if (elem_is_set (style, MSTYLE_FONT_NAME))
215 hash ^= GPOINTER_TO_UINT (style->font_detail.name);
216 MIX (hash);
218 if (elem_is_set (style, MSTYLE_FONT_BOLD))
219 hash ^= (style->font_detail.bold ? 1 : 2);
220 MIX (hash);
222 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
223 hash ^= (style->font_detail.italic ? 1 : 2);
224 MIX (hash);
226 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
227 hash ^= (style->font_detail.underline ? 1 : 2);
228 MIX (hash);
230 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
231 hash ^= (style->font_detail.strikethrough ? 1 : 2);
232 MIX (hash);
234 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
235 hash ^= (style->font_detail.script + 0x100);
236 MIX (hash);
238 if (elem_is_set (style, MSTYLE_FONT_SIZE))
239 hash ^= ((int)(style->font_detail.size * 97));
240 MIX (hash);
242 if (elem_is_set (style, MSTYLE_FORMAT))
243 hash ^= GPOINTER_TO_UINT (style->format);
244 MIX (hash);
246 if (elem_is_set (style, MSTYLE_ALIGN_H))
247 hash ^= (style->h_align + 0x100);
248 MIX (hash);
250 if (elem_is_set (style, MSTYLE_ALIGN_V))
251 hash ^= (style->v_align + 0x100);
252 MIX (hash);
254 if (elem_is_set (style, MSTYLE_INDENT))
255 hash ^= style->indent;
256 MIX (hash);
258 if (elem_is_set (style, MSTYLE_ROTATION))
259 hash ^= style->rotation;
260 MIX (hash);
262 if (elem_is_set (style, MSTYLE_TEXT_DIR))
263 hash ^= (style->text_dir + 0x100);
264 MIX (hash);
266 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
267 hash ^= (style->wrap_text ? 1 : 2);
268 MIX (hash);
270 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
271 hash ^= (style->shrink_to_fit ? 1 : 2);
272 MIX (hash);
274 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
275 hash ^= (style->contents_locked ? 1 : 2);
276 MIX (hash);
278 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
279 hash ^= (style->contents_hidden ? 1 : 2);
280 MIX (hash);
282 style->hash_key_xl = (guint32)hash;
284 /* From here on, fields are not in MS XL */
286 if (elem_is_set (style, MSTYLE_VALIDATION)) {
288 * The hash used must not depend on the expressions inside
289 * the validation.
291 hash ^= (style->validation != NULL ? 1 : 2);
293 MIX (hash);
295 if (elem_is_set (style, MSTYLE_HLINK))
296 hash ^= GPOINTER_TO_UINT (style->hlink);
297 MIX (hash);
299 if (elem_is_set (style, MSTYLE_INPUT_MSG))
300 hash ^= GPOINTER_TO_UINT (style->input_msg);
301 MIX (hash);
303 if (elem_is_set (style, MSTYLE_CONDITIONS)) {
305 * The hash used must not depend on the expressions inside
306 * the conditions.
308 hash ^= style->conditions
309 ? gnm_style_conditions_hash (style->conditions)
310 : 1u;
312 MIX (hash);
314 style->hash_key = (guint32)hash;
316 if (G_UNLIKELY (style->set == 0)) {
318 * gnm_style_new and gnm_style_dup both assume that the
319 * correct hash values (both of them) for the empty style
320 * is zero.
322 g_assert (style->hash_key == 0);
323 g_assert (style->hash_key_xl == 0);
327 #undef MIX
329 guint
330 gnm_style_hash_XL (gconstpointer style)
332 if (((GnmStyle const *)style)->changed)
333 gnm_style_update ((GnmStyle *)style);
334 return ((GnmStyle const *)style)->hash_key_xl;
337 guint
338 gnm_style_hash (gconstpointer style)
340 if (((GnmStyle const *)style)->changed)
341 gnm_style_update ((GnmStyle *)style);
342 return ((GnmStyle const *)style)->hash_key;
345 #define ELEM_IS_EQ(a,b,elem) \
346 (elem == MSTYLE_COLOR_BACK \
347 ? a->color.back == b->color.back || (a->color.back->is_auto && b->color.back->is_auto) \
348 : (elem == MSTYLE_COLOR_PATTERN \
349 ? a->color.pattern == b->color.pattern || (a->color.pattern->is_auto && b->color.pattern->is_auto) \
350 : (elem >= MSTYLE_BORDER_TOP && elem <= MSTYLE_BORDER_DIAGONAL) \
351 ? a->borders[elem - MSTYLE_BORDER_TOP] == b->borders[elem - MSTYLE_BORDER_TOP] \
352 : (elem == MSTYLE_PATTERN \
353 ? a->pattern == b->pattern \
354 : (elem == MSTYLE_FONT_COLOR \
355 ? a->color.font == b->color.font || (a->color.font->is_auto && b->color.font->is_auto) \
356 : (elem == MSTYLE_FONT_NAME \
357 ? a->font_detail.name == b->font_detail.name \
358 : (elem == MSTYLE_FONT_BOLD \
359 ? a->font_detail.bold == b->font_detail.bold \
360 : (elem == MSTYLE_FONT_ITALIC \
361 ? a->font_detail.italic == b->font_detail.italic \
362 : (elem == MSTYLE_FONT_UNDERLINE \
363 ? a->font_detail.underline == b->font_detail.underline \
364 : (elem == MSTYLE_FONT_STRIKETHROUGH \
365 ? a->font_detail.strikethrough == b->font_detail.strikethrough \
366 : (elem == MSTYLE_FONT_SCRIPT \
367 ? a->font_detail.script == b->font_detail.script \
368 : (elem == MSTYLE_FONT_SIZE \
369 ? a->font_detail.size == b->font_detail.size \
370 : (elem == MSTYLE_FORMAT \
371 ? a->format == b->format \
372 : (elem == MSTYLE_ALIGN_V \
373 ? a->v_align == b->v_align \
374 : (elem == MSTYLE_ALIGN_H \
375 ? a->h_align == b->h_align \
376 : (elem == MSTYLE_INDENT \
377 ? a->indent == b->indent \
378 : (elem == MSTYLE_ROTATION \
379 ? a->rotation == b->rotation \
380 : (elem == MSTYLE_TEXT_DIR \
381 ? a->text_dir == b->text_dir \
382 : (elem == MSTYLE_WRAP_TEXT \
383 ? a->wrap_text == b->wrap_text \
384 : (elem == MSTYLE_SHRINK_TO_FIT \
385 ? a->shrink_to_fit == b->shrink_to_fit \
386 : (elem == MSTYLE_CONTENTS_LOCKED \
387 ? a->contents_locked == b->contents_locked \
388 : (elem == MSTYLE_CONTENTS_HIDDEN \
389 ? a->contents_hidden == b->contents_hidden \
390 : (elem == MSTYLE_VALIDATION \
391 ? a->validation == b->validation \
392 : (elem == MSTYLE_HLINK \
393 ? a->hlink == b->hlink \
394 : (elem == MSTYLE_INPUT_MSG \
395 ? a->input_msg == b->input_msg \
396 : (elem == MSTYLE_CONDITIONS \
397 ? (a->conditions == b->conditions || \
398 (a->conditions && b->conditions && \
399 gnm_style_conditions_equal (a->conditions, b->conditions))) \
400 : FALSE)))))))))))))))))))))))))
403 * Note: the above is suboptimal for validation, hlink, input_msg.
405 * We are comparing pointers (which at least safely matches what we do
406 * with the hash), but I think we want proper equality.
409 static gboolean
410 elem_is_eq (GnmStyle const *a, GnmStyle const *b, GnmStyleElement elem)
412 return ELEM_IS_EQ (a, b, elem);
415 static void
416 elem_assign_contents (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
418 #ifdef DEBUG_STYLES
419 g_return_if_fail (src != dst);
420 g_return_if_fail (elem_is_set (src, elem));
421 #endif
422 switch (elem) {
423 case MSTYLE_COLOR_BACK : style_color_ref (dst->color.back = src->color.back); return;
424 case MSTYLE_COLOR_PATTERN : style_color_ref (dst->color.pattern = src->color.pattern); return;
425 case MSTYLE_ANY_BORDER:
426 elem -= MSTYLE_BORDER_TOP;
427 gnm_style_border_ref (dst->borders[elem] = src->borders[elem]);
428 return;
429 case MSTYLE_PATTERN: dst->pattern = src->pattern; return;
430 case MSTYLE_FONT_COLOR : style_color_ref (dst->color.font = src->color.font); return;
431 case MSTYLE_FONT_NAME: go_string_ref (dst->font_detail.name = src->font_detail.name); return;
432 case MSTYLE_FONT_BOLD: dst->font_detail.bold = src->font_detail.bold; return;
433 case MSTYLE_FONT_ITALIC: dst->font_detail.italic = src->font_detail.italic; return;
434 case MSTYLE_FONT_UNDERLINE: dst->font_detail.underline = src->font_detail.underline; return;
435 case MSTYLE_FONT_STRIKETHROUGH: dst->font_detail.strikethrough = src->font_detail.strikethrough; return;
436 case MSTYLE_FONT_SCRIPT: dst->font_detail.script = src->font_detail.script; return;
437 case MSTYLE_FONT_SIZE: dst->font_detail.size = src->font_detail.size; return;
438 case MSTYLE_FORMAT: go_format_ref (dst->format = src->format); return;
439 case MSTYLE_ALIGN_V: dst->v_align = src->v_align; return;
440 case MSTYLE_ALIGN_H: dst->h_align = src->h_align; return;
441 case MSTYLE_INDENT: dst->indent = src->indent; return;
442 case MSTYLE_ROTATION: dst->rotation = src->rotation; return;
443 case MSTYLE_TEXT_DIR: dst->text_dir = src->text_dir; return;
444 case MSTYLE_WRAP_TEXT: dst->wrap_text = src->wrap_text; return;
445 case MSTYLE_SHRINK_TO_FIT: dst->shrink_to_fit = src->shrink_to_fit; return;
446 case MSTYLE_CONTENTS_LOCKED: dst->contents_locked = src->contents_locked; return;
447 case MSTYLE_CONTENTS_HIDDEN: dst->contents_hidden = src->contents_hidden; return;
448 case MSTYLE_VALIDATION:
449 if ((dst->validation = src->validation))
450 gnm_validation_ref (dst->validation);
451 return;
452 case MSTYLE_HLINK:
453 if ((dst->hlink = src->hlink))
454 g_object_ref (dst->hlink);
455 return;
456 case MSTYLE_INPUT_MSG:
457 if ((dst->input_msg = src->input_msg))
458 g_object_ref (dst->input_msg);
459 return;
460 case MSTYLE_CONDITIONS:
461 if ((dst->conditions = src->conditions))
462 g_object_ref (dst->conditions);
463 return;
464 default:
469 static void
470 elem_clear_contents (GnmStyle *style, GnmStyleElement elem)
472 #ifdef DEBUG_STYLES
473 g_return_if_fail (style != NULL);
474 #endif
475 if (!elem_is_set (style, elem))
476 return;
478 switch (elem) {
479 case MSTYLE_COLOR_BACK : style_color_unref (style->color.back); return;
480 case MSTYLE_COLOR_PATTERN : style_color_unref (style->color.pattern); return;
481 case MSTYLE_ANY_BORDER:
482 gnm_style_border_unref (style->borders[elem - MSTYLE_BORDER_TOP]);
483 return;
484 case MSTYLE_FONT_COLOR : style_color_unref (style->color.font); return;
485 case MSTYLE_FONT_NAME: go_string_unref (style->font_detail.name); return;
486 case MSTYLE_FORMAT: go_format_unref (style->format); return;
487 case MSTYLE_VALIDATION:
488 if (style->validation)
489 gnm_validation_unref (style->validation);
490 return;
491 case MSTYLE_HLINK:
492 if (style->hlink)
493 g_object_unref (style->hlink);
494 return;
495 case MSTYLE_INPUT_MSG:
496 if (style->input_msg)
497 g_object_unref (style->input_msg);
498 return;
499 case MSTYLE_CONDITIONS:
500 if (style->conditions) {
501 clear_conditional_merges (style);
502 g_object_unref (style->conditions);
504 return;
505 default:
511 * gnm_style_find_conflicts :
512 * @accum: accumulator #GnmStyle
513 * @overlay: #GnmStyle
514 * @conflicts: flags
516 * Copy any items from @overlay that do not conflict with the values in @accum.
517 * If an element had a previous conflict (flagged via @conflicts) it is ignored.
519 * Returns @conflicts with any new conflicts added.
521 unsigned int
522 gnm_style_find_conflicts (GnmStyle *accum, GnmStyle const *overlay,
523 unsigned int conflicts)
525 int i;
527 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (conflicts));
529 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
530 if (conflicts & (1u << i) || !elem_is_set (overlay, i)) {
531 /* Nothing */
532 } else if (!elem_is_set (accum, i)) {
533 elem_assign_contents (accum, overlay, i);
534 elem_set (accum, i);
535 elem_changed (accum, i);
536 } else if (!elem_is_eq (accum, overlay, i))
537 conflicts |= (1u << i);
540 return conflicts;
544 * gnm_style_find_differences:
545 * @a: A #GnmStyle
546 * @b: A #GnmStyle
547 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
549 * Determine how two fully-qualified styles differ.
551 * Returns differences as a bitset of #GnmStyleElement.
553 unsigned int
554 gnm_style_find_differences (GnmStyle const *a, GnmStyle const *b,
555 gboolean relax_sheet)
557 int i;
558 unsigned int diffs = 0;
560 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (diffs));
562 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
563 if (elem_is_set (a, i) != elem_is_set (b, i) ||
564 (elem_is_set (a, i) && !elem_is_eq (a, b, i)))
565 diffs |= (1u << i);
568 if (relax_sheet) {
569 if (a->hlink && b->hlink &&
570 gnm_hlink_equal (a->hlink, b->hlink, relax_sheet))
571 diffs &= ~(1u << MSTYLE_HLINK);
573 // FIXME: Validations
575 // FIXME: Conditions
578 return diffs;
582 static inline void
583 gnm_style_clear_pango (GnmStyle *style)
585 if (style->pango_attrs) {
586 pango_attr_list_unref (style->pango_attrs);
587 style->pango_attrs = NULL;
592 static inline void
593 gnm_style_clear_font (GnmStyle *style)
595 if (style->font) {
596 gnm_font_unref (style->font);
597 style->font = NULL;
599 g_clear_object (&style->font_context);
603 * gnm_style_new :
605 * Caller is responsible for unrefing the result.
607 * Returns a new style with _no_ elements set.
609 GnmStyle *
610 gnm_style_new (void)
612 GnmStyle *style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
614 style->ref_count = 1;
615 style->link_count = 0;
616 style->linked_sheet = NULL;
617 style->pango_attrs = NULL;
618 style->font = NULL;
619 style->validation = NULL;
621 style->set = style->changed = 0;
622 style->validation = NULL;
623 style->hlink = NULL;
624 style->input_msg = NULL;
625 style->conditions = NULL;
627 d(("new %p\n", style));
629 return style;
633 * gnm_style_new_default:
635 * Caller is responsible for unrefing the result.
637 * Return value: a new style initialized to the default state.
639 GnmStyle *
640 gnm_style_new_default (void)
642 GnmStyle *new_style = gnm_style_new ();
643 int i;
645 gnm_style_set_font_name (new_style, gnm_conf_get_core_defaultfont_name ());
646 gnm_style_set_font_size (new_style, gnm_conf_get_core_defaultfont_size ());
647 gnm_style_set_font_bold (new_style, gnm_conf_get_core_defaultfont_bold ());
648 gnm_style_set_font_italic (new_style, gnm_conf_get_core_defaultfont_italic ());
650 gnm_style_set_format (new_style, go_format_general ());
651 gnm_style_set_align_v (new_style, GNM_VALIGN_BOTTOM);
652 gnm_style_set_align_h (new_style, GNM_HALIGN_GENERAL);
653 gnm_style_set_indent (new_style, 0);
654 gnm_style_set_rotation (new_style, 0);
655 gnm_style_set_text_dir (new_style, GNM_TEXT_DIR_CONTEXT);
656 gnm_style_set_wrap_text (new_style, FALSE);
657 gnm_style_set_shrink_to_fit (new_style, FALSE);
658 gnm_style_set_contents_locked (new_style, TRUE);
659 gnm_style_set_contents_hidden (new_style, FALSE);
660 gnm_style_set_font_uline (new_style, UNDERLINE_NONE);
661 gnm_style_set_font_strike (new_style, FALSE);
662 gnm_style_set_font_script (new_style, GO_FONT_SCRIPT_STANDARD);
664 gnm_style_set_validation (new_style, NULL);
665 gnm_style_set_hlink (new_style, NULL);
666 gnm_style_set_input_msg (new_style, NULL);
667 gnm_style_set_conditions (new_style, NULL);
669 gnm_style_set_font_color (new_style, style_color_black ());
670 gnm_style_set_back_color (new_style, style_color_auto_back ());
671 gnm_style_set_pattern_color (new_style, style_color_black ());
673 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
674 gnm_style_set_border (new_style, i,
675 gnm_style_border_ref (gnm_style_border_none ()));
676 gnm_style_set_pattern (new_style, 0);
678 return new_style;
681 GnmStyle *
682 gnm_style_dup (GnmStyle const *src)
684 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
685 int i;
687 new_style->ref_count = 1;
688 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
689 if (elem_is_set (src, i)) {
690 elem_assign_contents (new_style, src, i);
691 elem_set (new_style, i);
692 elem_changed (new_style, i);
695 if ((new_style->pango_attrs = src->pango_attrs)) {
696 pango_attr_list_ref (new_style->pango_attrs);
697 new_style->pango_attrs_zoom = src->pango_attrs_zoom;
700 if ((new_style->font = src->font)) {
701 gnm_font_ref (new_style->font);
702 new_style->font_context = g_object_ref (src->font_context);
705 d(("dup %p\n", new_style));
706 return new_style;
710 * gnm_style_new_merged :
711 * @base: #GnmStyle
712 * @overlay: #GnmStyle
714 * A new GnmStyle that contains any elements of @overlay that are set, and uses
715 * @base for anything that is not set in @overlay.
717 * Returns: (transfer full): A ref to a new GnmStyle.
719 GnmStyle *
720 gnm_style_new_merged (GnmStyle const *base, GnmStyle const *overlay)
722 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
723 int i;
725 new_style->ref_count = 1;
726 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
727 if (elem_is_set (overlay, i))
728 elem_assign_contents (new_style, overlay, i);
729 else if (elem_is_set (base, i))
730 elem_assign_contents (new_style, base, i);
731 else
732 continue;
733 elem_set (new_style, i);
734 elem_changed (new_style, i);
736 d(("copy merge %p\n", new_style));
737 return new_style;
740 void
741 gnm_style_ref (GnmStyle const *style)
743 g_return_if_fail (style != NULL);
744 g_return_if_fail (style->ref_count > 0);
746 ((GnmStyle *)style)->ref_count++;
747 d(("ref %p = %d\n", style, style->ref_count));
751 * gnm_style_unref :
752 * @style: #GnmStyle const
754 * Unrefs and _potentially frees_ @style.
755 * Takes a _const_ pointer to facilitate life cycles. The const indicates that
756 * the content can not be changed, mainly when handling styles that are in the
757 * style hash.
759 void
760 gnm_style_unref (GnmStyle const *style)
762 g_return_if_fail (style != NULL);
763 g_return_if_fail (style->ref_count > 0);
765 d(("unref %p = %d\n", style, style->ref_count-1));
766 if (((GnmStyle *)style)->ref_count-- <= 1) {
767 GnmStyle *unconst = (GnmStyle *)style;
768 int i;
770 g_return_if_fail (style->link_count == 0);
771 g_return_if_fail (style->linked_sheet == NULL);
773 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
774 elem_clear_contents (unconst, i);
775 unconst->set = 0;
776 clear_conditional_merges (unconst);
777 gnm_style_clear_pango (unconst);
778 gnm_style_clear_font (unconst);
780 if (style->deps) {
781 if (style->deps->len > 0)
782 g_warning ("Leftover style deps!");
783 g_ptr_array_free (style->deps, TRUE);
786 CHUNK_FREE (gnm_style_pool, unconst);
790 GType
791 gnm_style_get_type (void)
793 static GType t = 0;
795 if (t == 0) {
796 t = g_boxed_type_register_static ("GnmStyle",
797 (GBoxedCopyFunc)gnm_style_ref,
798 (GBoxedFreeFunc)gnm_style_unref);
800 return t;
804 * Replace auto pattern color in style with sheet's auto pattern color.
805 * make_copy tells if we are allowed to modify the style in place or we must
806 * make a copy first.
808 static GnmStyle *
809 link_pattern_color (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
811 GnmColor *pattern_color = style->color.pattern;
813 if (pattern_color->is_auto && auto_color != pattern_color) {
814 style_color_ref (auto_color);
815 if (make_copy) {
816 GnmStyle *orig = style;
817 style = gnm_style_dup (style);
818 gnm_style_unref (orig);
820 gnm_style_set_pattern_color (style, auto_color);
822 return style;
826 * Replace auto border colors in style with sheet's auto pattern
827 * color. (pattern is *not* a typo.)
828 * make_copy tells if we are allowed to modify the style in place or we must
829 * make a copy first.
831 * FIXME: We conjecture that XL color 64 in border should change with the
832 * pattern, but not color 127. That distinction is not yet represented in
833 * our data structures.
835 static GnmStyle *
836 link_border_colors (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
838 int i;
840 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i) {
841 if (elem_is_set (style, i)) {
842 GnmBorder *border =
843 style->borders[i- MSTYLE_BORDER_TOP];
844 GnmColor *color;
846 if (!border)
847 continue;
849 color = border->color;
850 if (color->is_auto && auto_color != color) {
851 GnmBorder *new_border;
852 GnmStyleBorderOrientation orientation;
854 switch (i) {
855 case MSTYLE_BORDER_LEFT:
856 case MSTYLE_BORDER_RIGHT:
857 orientation = GNM_STYLE_BORDER_VERTICAL;
858 break;
859 case MSTYLE_BORDER_REV_DIAGONAL:
860 case MSTYLE_BORDER_DIAGONAL:
861 orientation = GNM_STYLE_BORDER_DIAGONAL;
862 break;
863 case MSTYLE_BORDER_TOP:
864 case MSTYLE_BORDER_BOTTOM:
865 default:
866 orientation = GNM_STYLE_BORDER_HORIZONTAL;
867 break;
869 style_color_ref (auto_color);
870 new_border = gnm_style_border_fetch (
871 border->line_type, auto_color,
872 orientation);
874 if (make_copy) {
875 GnmStyle *orig = style;
876 style = gnm_style_dup (style);
877 gnm_style_unref (orig);
878 make_copy = FALSE;
880 gnm_style_set_border (style, i, new_border);
884 return style;
887 static void
888 gnm_style_linked_sheet_changed (GnmStyle *style)
890 Sheet *sheet = style->linked_sheet;
892 if (elem_is_set (style, MSTYLE_VALIDATION) &&
893 style->validation &&
894 gnm_validation_get_sheet (style->validation) != sheet) {
895 GnmValidation *new_v = gnm_validation_dup (style->validation);
896 gnm_validation_set_sheet (new_v, sheet);
897 gnm_style_set_validation (style, new_v);
900 if (elem_is_set (style, MSTYLE_HLINK) &&
901 style->hlink &&
902 gnm_hlink_get_sheet (style->hlink) != sheet) {
903 GnmHLink *new_l = gnm_hlink_dup (style->hlink);
904 gnm_hlink_set_sheet (new_l, sheet);
905 gnm_style_set_hlink (style, new_l);
908 if (elem_is_set (style, MSTYLE_CONDITIONS) &&
909 style->conditions &&
910 gnm_style_conditions_get_sheet (style->conditions) != sheet) {
911 GnmStyleConditions *new_c = gnm_style_conditions_dup (style->conditions);
912 gnm_style_conditions_set_sheet (new_c, sheet);
913 gnm_style_set_conditions (style, new_c);
918 * gnm_style_link_sheet :
919 * @style:
920 * @sheet:
922 * ABSORBS a reference to the style and sets the link count to 1.
924 * Where auto pattern color occurs in the style (it may for pattern and
925 * borders), it is replaced with the sheet's auto pattern color. We make
926 * sure that we do not modify the style which was passed in to us, but also
927 * that we don't copy more than once. The final argument to the
928 * link_xxxxx_color functions tell whether or not to copy.
930 GnmStyle *
931 gnm_style_link_sheet (GnmStyle *style, Sheet *sheet)
933 GnmColor *auto_color;
934 gboolean style_is_orig = TRUE;
936 if (style->linked_sheet != NULL) {
937 GnmStyle *orig = style;
938 style = gnm_style_dup (style);
939 gnm_style_unref (orig);
940 style_is_orig = FALSE;
942 /* safety test */
943 g_return_val_if_fail (style->linked_sheet != sheet, style);
946 g_return_val_if_fail (style->link_count == 0, style);
947 g_return_val_if_fail (style->linked_sheet == NULL, style);
949 auto_color = sheet_style_get_auto_pattern_color (sheet);
950 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
951 style = link_pattern_color (style, auto_color, style_is_orig);
952 style = link_border_colors (style, auto_color, style_is_orig);
953 style_color_unref (auto_color);
955 style->linked_sheet = sheet;
956 style->link_count = 1;
958 gnm_style_linked_sheet_changed (style);
960 d(("link sheet %p = 1\n", style));
961 return style;
964 void
965 gnm_style_link (GnmStyle *style)
967 g_return_if_fail (style->link_count > 0);
969 style->link_count++;
970 d(("link %p = %d\n", style, style->link_count));
973 void
974 gnm_style_link_multiple (GnmStyle *style, int count)
976 g_return_if_fail (style->link_count > 0);
978 style->link_count += count;
979 d(("multiple link %p + %d = %d\n", style, count, style->link_count));
982 void
983 gnm_style_unlink (GnmStyle *style)
985 g_return_if_fail (style->link_count > 0);
987 d(("unlink %p = %d\n", style, style->link_count-1));
988 if (style->link_count-- == 1) {
989 sheet_style_unlink (style->linked_sheet, style);
990 style->linked_sheet = NULL;
991 gnm_style_unref (style);
995 gboolean
996 gnm_style_eq (GnmStyle const *a, GnmStyle const *b)
998 return a == b;
1001 gboolean
1002 gnm_style_equal (GnmStyle const *a, GnmStyle const *b)
1004 int i;
1006 if (a == b)
1007 return TRUE;
1008 if (a->set != b->set || !gnm_style_equal_XL (a, b))
1009 return FALSE;
1010 UNROLLED_FOR (i = MSTYLE_VALIDATION, i < MSTYLE_ELEMENT_MAX, i++, {
1011 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1012 return FALSE;
1015 return TRUE;
1018 gboolean
1019 gnm_style_equal_XL (GnmStyle const *a, GnmStyle const *b)
1021 int i;
1023 g_return_val_if_fail (a != NULL, FALSE);
1024 g_return_val_if_fail (b != NULL, FALSE);
1026 if (a == b)
1027 return TRUE;
1029 if ((a->set ^ b->set) & ((1u << MSTYLE_VALIDATION) - 1))
1030 return FALSE;
1032 UNROLLED_FOR (i = MSTYLE_COLOR_BACK, i < MSTYLE_VALIDATION, i++, {
1033 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1034 return FALSE;
1036 return TRUE;
1040 * gnm_style_equal_elem:
1041 * @a: first style
1042 * @b: second style
1043 * @e: style element
1045 * Returns: %TRUE, if the two styles have the same contents for the
1046 * given element, either because neither have it set, or because both
1047 * have it set and to the same value.
1049 gboolean
1050 gnm_style_equal_elem (GnmStyle const *a, GnmStyle const *b, GnmStyleElement e)
1052 if (elem_is_set (a, e))
1053 return elem_is_set (b, e) && elem_is_eq (a, b, e);
1054 else
1055 return !elem_is_set (b, e);
1060 #define CMP_TRY_NUMBER_RAW(a_,b_) \
1061 do { \
1062 if ((a_) < (b_)) return -1; \
1063 if ((a_) > (b_)) return -1; \
1064 } while (0)
1066 #define CMP_TRY_NUMBER(e_,f_) \
1067 do { \
1068 if (elem_is_set (a, (e_))) \
1069 CMP_TRY_NUMBER_RAW(a->f_, b->f_); \
1070 } while (0)
1072 #define CMP_TRY_COLOR(e_,f_) \
1073 do { \
1074 if (elem_is_set (a, (e_))) { \
1075 CMP_TRY_NUMBER_RAW(a->f_->is_auto, b->f_->is_auto); \
1076 CMP_TRY_NUMBER_RAW(a->f_->go_color, b->f_->go_color); \
1078 } while (0)
1081 * Ordering of GnmStyles. Apart from FIXMEs, this shouldn't change
1082 * from one run to the next.
1085 gnm_style_cmp (GnmStyle const *a, GnmStyle const *b)
1087 GnmStyleElement e;
1089 if (a == b)
1090 return 0;
1093 * Very quick comparison based on what is set. This also allows
1094 * us to check on one elem_is_set below.
1096 CMP_TRY_NUMBER_RAW (a->set, b->set);
1098 CMP_TRY_COLOR (MSTYLE_FONT_COLOR, color.font);
1099 CMP_TRY_COLOR (MSTYLE_COLOR_BACK, color.back);
1100 CMP_TRY_COLOR (MSTYLE_COLOR_PATTERN, color.pattern);
1101 for (e = MSTYLE_BORDER_TOP; e <= MSTYLE_BORDER_DIAGONAL; e++) {
1102 GnmBorder const *ba, *bb;
1103 if (!elem_is_set (a, e))
1104 continue;
1105 ba = a->borders[e - MSTYLE_BORDER_TOP];
1106 bb = b->borders[e - MSTYLE_BORDER_TOP];
1107 if (ba == bb)
1108 continue; /* Handles both being NULL */
1109 CMP_TRY_NUMBER_RAW(!!ba, !!bb);
1110 CMP_TRY_NUMBER_RAW(ba->line_type, bb->line_type);
1111 CMP_TRY_NUMBER_RAW(ba->color->go_color, bb->color->go_color);
1112 CMP_TRY_NUMBER_RAW(ba->begin_margin, bb->begin_margin);
1113 CMP_TRY_NUMBER_RAW(ba->end_margin, bb->end_margin);
1114 CMP_TRY_NUMBER_RAW(ba->width, bb->width);
1116 CMP_TRY_NUMBER (MSTYLE_PATTERN, pattern);
1117 if (elem_is_set (a, MSTYLE_FONT_NAME)) {
1118 /* Plain strcmp, not utf-8. We need to see diffs. */
1119 int tmp = strcmp (a->font_detail.name->str,
1120 b->font_detail.name->str);
1121 if (tmp)
1122 return tmp;
1124 CMP_TRY_NUMBER (MSTYLE_FONT_BOLD, font_detail.bold);
1125 CMP_TRY_NUMBER (MSTYLE_FONT_ITALIC, font_detail.italic);
1126 CMP_TRY_NUMBER (MSTYLE_FONT_UNDERLINE, font_detail.underline);
1127 CMP_TRY_NUMBER (MSTYLE_FONT_STRIKETHROUGH, font_detail.strikethrough);
1128 CMP_TRY_NUMBER (MSTYLE_FONT_SCRIPT, font_detail.script);
1129 CMP_TRY_NUMBER (MSTYLE_FONT_SIZE, font_detail.size);
1130 if (elem_is_set (a, MSTYLE_FORMAT)) {
1131 /* Plain strcmp, not utf-8. We need to see diffs. */
1132 int tmp = strcmp (go_format_as_XL (a->format),
1133 go_format_as_XL (b->format));
1134 if (tmp)
1135 return tmp;
1137 CMP_TRY_NUMBER (MSTYLE_ALIGN_H, h_align);
1138 CMP_TRY_NUMBER (MSTYLE_ALIGN_V, v_align);
1139 CMP_TRY_NUMBER (MSTYLE_INDENT, indent);
1140 CMP_TRY_NUMBER (MSTYLE_ROTATION, rotation);
1141 CMP_TRY_NUMBER (MSTYLE_TEXT_DIR, text_dir);
1142 CMP_TRY_NUMBER (MSTYLE_WRAP_TEXT, wrap_text);
1143 CMP_TRY_NUMBER (MSTYLE_SHRINK_TO_FIT, shrink_to_fit);
1144 CMP_TRY_NUMBER (MSTYLE_CONTENTS_LOCKED, contents_locked);
1145 CMP_TRY_NUMBER (MSTYLE_CONTENTS_HIDDEN, contents_hidden);
1146 /* FIXME: validation */
1147 /* FIXME: hlink */
1148 /* FIXME: input_msg */
1149 /* FIXME: conditions */
1150 /* FIXME: cond_styles */
1152 /* Last resort: pointer comparison. */
1153 return a < b ? -1 : +1;
1156 #undef CMP_TRY_NUMBER
1157 #undef CMP_TRY_COLOR
1161 * gnm_style_equal_header :
1162 * @a: #GnmStyle
1163 * @b: #GnmStyle
1164 * @top: is this a header vertically or horizontally
1166 * Check to see if @a is different enough from @b to make us think that @a is
1167 * from a header.
1169 gboolean
1170 gnm_style_equal_header (GnmStyle const *a, GnmStyle const *b, gboolean top)
1172 int i = top ? MSTYLE_BORDER_BOTTOM : MSTYLE_BORDER_RIGHT;
1174 if (!elem_is_eq (a, b, i))
1175 return FALSE;
1176 for (i = MSTYLE_COLOR_BACK; i <= MSTYLE_COLOR_PATTERN ; i++)
1177 if (!elem_is_eq (a, b, i))
1178 return FALSE;
1179 for (i = MSTYLE_FONT_COLOR; i <= MSTYLE_SHRINK_TO_FIT ; i++)
1180 if (!elem_is_eq (a, b, i))
1181 return FALSE;
1182 return TRUE;
1186 gboolean
1187 gnm_style_is_element_set (GnmStyle const *style, GnmStyleElement elem)
1189 g_return_val_if_fail (style != NULL, FALSE);
1190 g_return_val_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX, FALSE);
1191 return elem_is_set (style, elem);
1195 * gnm_style_is_complete :
1196 * @style: #GnmStyle
1198 * Returns TRUE if all elements are set.
1200 gboolean
1201 gnm_style_is_complete (GnmStyle const *style)
1203 g_return_val_if_fail (style != NULL, FALSE);
1205 return style->set == ((1u << MSTYLE_ELEMENT_MAX) - 1);
1208 void
1209 gnm_style_unset_element (GnmStyle *style, GnmStyleElement elem)
1211 g_return_if_fail (style != NULL);
1212 g_return_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX);
1214 if (elem_is_set (style, elem)) {
1215 elem_clear_contents (style, elem);
1216 elem_unset (style, elem);
1221 * gnm_style_merge :
1222 * @base: #GnmStyle
1223 * @overlay: #GnmStyle
1225 * Applies all active elements of @overlay onto @base.
1227 void
1228 gnm_style_merge (GnmStyle *base, GnmStyle const *overlay)
1230 unsigned i;
1231 if (base == overlay)
1232 return;
1233 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
1234 if (elem_is_set (overlay, i)) {
1235 elem_clear_contents (base, i);
1236 elem_assign_contents (base, overlay, i);
1237 elem_changed (base, i);
1242 * gnm_style_merge_element:
1243 * @dst: Destination style
1244 * @src: Source style
1245 * @elem: Element to replace
1247 * This function replaces element @elem in style @dst with element @elem
1248 * in style @src. (If element @elem was already set in style @dst then
1249 * the element will first be unset)
1251 void
1252 gnm_style_merge_element (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
1254 g_return_if_fail (src != NULL);
1255 g_return_if_fail (dst != NULL);
1256 g_return_if_fail (src != dst);
1258 if (elem_is_set (src, elem)) {
1259 elem_clear_contents (dst, elem);
1260 elem_assign_contents (dst, src, elem);
1261 elem_set (dst, elem);
1262 elem_changed (dst, elem);
1266 void
1267 gnm_style_set_font_color (GnmStyle *style, GnmColor *col)
1269 g_return_if_fail (style != NULL);
1270 g_return_if_fail (col != NULL);
1272 elem_changed (style, MSTYLE_FONT_COLOR);
1273 if (elem_is_set (style, MSTYLE_FONT_COLOR))
1274 style_color_unref (style->color.font);
1275 else
1276 elem_set (style, MSTYLE_FONT_COLOR);
1277 elem_changed (style, MSTYLE_FONT_COLOR);
1278 style->color.font = col;
1279 gnm_style_clear_pango (style);
1283 * gnm_style_set_back_color :
1284 * @style: #GnmStyle
1285 * @col: #GnmColor
1287 * Assigns @col as the background of @style.
1289 * NOTE : the background colour is only visibile if
1290 * GnmStyle::pattern > 0
1292 void
1293 gnm_style_set_back_color (GnmStyle *style, GnmColor *col)
1295 g_return_if_fail (style != NULL);
1296 g_return_if_fail (col != NULL);
1298 elem_changed (style, MSTYLE_COLOR_BACK);
1299 if (elem_is_set (style, MSTYLE_COLOR_BACK))
1300 style_color_unref (style->color.back);
1301 else
1302 elem_set (style, MSTYLE_COLOR_BACK);
1303 style->color.back = col;
1304 gnm_style_clear_pango (style);
1306 void
1307 gnm_style_set_pattern_color (GnmStyle *style, GnmColor *col)
1309 g_return_if_fail (style != NULL);
1310 g_return_if_fail (col != NULL);
1312 elem_changed (style, MSTYLE_COLOR_PATTERN);
1313 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1314 style_color_unref (style->color.pattern);
1315 else
1316 elem_set (style, MSTYLE_COLOR_PATTERN);
1317 style->color.pattern = col;
1318 gnm_style_clear_pango (style);
1321 GnmColor *
1322 gnm_style_get_font_color (GnmStyle const *style)
1324 g_return_val_if_fail (style != NULL, NULL);
1325 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_COLOR), NULL);
1326 return style->color.font;
1329 GnmColor *
1330 gnm_style_get_back_color (GnmStyle const *style)
1332 g_return_val_if_fail (style != NULL, NULL);
1333 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_BACK), NULL);
1334 return style->color.back;
1337 GnmColor *
1338 gnm_style_get_pattern_color (GnmStyle const *style)
1340 g_return_val_if_fail (style != NULL, NULL);
1341 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_PATTERN), NULL);
1342 return style->color.pattern;
1345 void
1346 gnm_style_set_border (GnmStyle *style, GnmStyleElement elem,
1347 GnmBorder *border)
1349 g_return_if_fail (style != NULL);
1351 /* NOTE : It is legal for border to be NULL */
1352 switch (elem) {
1353 case MSTYLE_ANY_BORDER:
1354 elem_changed (style, elem);
1355 elem_set (style, elem);
1356 elem -= MSTYLE_BORDER_TOP;
1357 if (style->borders[elem])
1358 gnm_style_border_unref (style->borders[elem]);
1359 style->borders[elem] = border;
1360 break;
1361 default:
1362 g_warning ("Not a border element");
1363 break;
1367 GnmBorder *
1368 gnm_style_get_border (GnmStyle const *style, GnmStyleElement elem)
1370 g_return_val_if_fail (style != NULL, NULL);
1372 switch (elem) {
1373 case MSTYLE_ANY_BORDER:
1374 return style->borders[elem - MSTYLE_BORDER_TOP ];
1376 default:
1377 g_warning ("Not a border element");
1378 return NULL;
1382 void
1383 gnm_style_set_pattern (GnmStyle *style, int pattern)
1385 g_return_if_fail (style != NULL);
1386 g_return_if_fail (pattern >= 0);
1387 g_return_if_fail (pattern < GNM_PATTERNS_MAX);
1389 elem_changed (style, MSTYLE_PATTERN);
1390 elem_set (style, MSTYLE_PATTERN);
1391 style->pattern = pattern;
1395 gnm_style_get_pattern (GnmStyle const *style)
1397 g_return_val_if_fail (style != NULL, 0);
1398 g_return_val_if_fail (elem_is_set (style, MSTYLE_PATTERN), 0);
1400 return style->pattern;
1404 * gnm_style_get_font:
1405 * @style: #GnmStyle
1406 * @context: #PangoContext
1408 * Returns: (transfer none):
1410 GnmFont *
1411 gnm_style_get_font (GnmStyle const *style, PangoContext *context)
1413 g_return_val_if_fail (style != NULL, NULL);
1415 if (!style->font || style->font_context != context) {
1416 char const *name;
1417 gboolean bold, italic;
1418 double size;
1420 gnm_style_clear_font ((GnmStyle *)style);
1422 if (elem_is_set (style, MSTYLE_FONT_NAME))
1423 name = gnm_style_get_font_name (style);
1424 else
1425 name = DEFAULT_FONT;
1427 if (elem_is_set (style, MSTYLE_FONT_BOLD))
1428 bold = gnm_style_get_font_bold (style);
1429 else
1430 bold = FALSE;
1432 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
1433 italic = gnm_style_get_font_italic (style);
1434 else
1435 italic = FALSE;
1437 if (elem_is_set (style, MSTYLE_FONT_SIZE))
1438 size = gnm_style_get_font_size (style);
1439 else
1440 size = DEFAULT_SIZE;
1442 ((GnmStyle *)style)->font =
1443 gnm_font_new (context, name, size, bold, italic);
1444 ((GnmStyle *)style)->font_context = g_object_ref (context);
1447 return style->font;
1450 void
1451 gnm_style_set_font_name (GnmStyle *style, char const *name)
1453 g_return_if_fail (name != NULL);
1454 g_return_if_fail (style != NULL);
1456 elem_changed (style, MSTYLE_FONT_NAME);
1457 if (elem_is_set (style, MSTYLE_FONT_NAME))
1458 go_string_unref (style->font_detail.name);
1459 else
1460 elem_set (style, MSTYLE_FONT_NAME);
1461 style->font_detail.name = go_string_new (name);
1462 gnm_style_clear_font (style);
1463 gnm_style_clear_pango (style);
1466 char const *
1467 gnm_style_get_font_name (GnmStyle const *style)
1469 g_return_val_if_fail (style != NULL, NULL);
1470 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_NAME), NULL);
1472 return style->font_detail.name->str;
1475 void
1476 gnm_style_set_font_bold (GnmStyle *style, gboolean bold)
1478 g_return_if_fail (style != NULL);
1480 elem_changed (style, MSTYLE_FONT_BOLD);
1481 elem_set (style, MSTYLE_FONT_BOLD);
1482 style->font_detail.bold = !!bold;
1483 gnm_style_clear_font (style);
1484 gnm_style_clear_pango (style);
1487 gboolean
1488 gnm_style_get_font_bold (GnmStyle const *style)
1490 g_return_val_if_fail (style != NULL, FALSE);
1491 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_BOLD), FALSE);
1493 return style->font_detail.bold;
1496 void
1497 gnm_style_set_font_italic (GnmStyle *style, gboolean italic)
1499 g_return_if_fail (style != NULL);
1501 elem_changed (style, MSTYLE_FONT_ITALIC);
1502 elem_set (style, MSTYLE_FONT_ITALIC);
1503 style->font_detail.italic = !!italic;
1504 gnm_style_clear_font (style);
1505 gnm_style_clear_pango (style);
1508 gboolean
1509 gnm_style_get_font_italic (GnmStyle const *style)
1511 g_return_val_if_fail (style != NULL, FALSE);
1512 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_ITALIC), FALSE);
1514 return style->font_detail.italic;
1517 void
1518 gnm_style_set_font_uline (GnmStyle *style, GnmUnderline const underline)
1520 g_return_if_fail (style != NULL);
1521 g_return_if_fail (underline >= UNDERLINE_NONE && underline <= UNDERLINE_DOUBLE_LOW);
1523 elem_changed (style, MSTYLE_FONT_UNDERLINE);
1524 elem_set (style, MSTYLE_FONT_UNDERLINE);
1525 style->font_detail.underline = underline;
1526 gnm_style_clear_pango (style);
1529 GnmUnderline
1530 gnm_style_get_font_uline (GnmStyle const *style)
1532 g_return_val_if_fail (style != NULL, UNDERLINE_NONE);
1533 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_UNDERLINE), UNDERLINE_NONE);
1535 return style->font_detail.underline;
1538 void
1539 gnm_style_set_font_strike (GnmStyle *style, gboolean strikethrough)
1541 g_return_if_fail (style != NULL);
1543 elem_changed (style, MSTYLE_FONT_STRIKETHROUGH);
1544 elem_set (style, MSTYLE_FONT_STRIKETHROUGH);
1545 style->font_detail.strikethrough = !!strikethrough;
1546 gnm_style_clear_pango (style);
1549 gboolean
1550 gnm_style_get_font_strike (GnmStyle const *style)
1552 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
1554 return style->font_detail.strikethrough;
1557 void
1558 gnm_style_set_font_script (GnmStyle *style, GOFontScript script)
1560 g_return_if_fail (style != NULL);
1561 elem_changed (style, MSTYLE_FONT_SCRIPT);
1562 elem_set (style, MSTYLE_FONT_SCRIPT);
1563 style->font_detail.script = script;
1564 gnm_style_clear_pango (style);
1567 GOFontScript
1568 gnm_style_get_font_script (GnmStyle const *style)
1570 g_return_val_if_fail (style != NULL, GO_FONT_SCRIPT_STANDARD);
1571 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SCRIPT), GO_FONT_SCRIPT_STANDARD);
1573 return style->font_detail.script;
1576 void
1577 gnm_style_set_font_size (GnmStyle *style, double size)
1579 g_return_if_fail (style != NULL);
1580 g_return_if_fail (size >= 1.);
1581 elem_changed (style, MSTYLE_FONT_SIZE);
1582 elem_set (style, MSTYLE_FONT_SIZE);
1583 style->font_detail.size = size;
1584 gnm_style_clear_font (style);
1585 gnm_style_clear_pango (style);
1588 double
1589 gnm_style_get_font_size (GnmStyle const *style)
1591 g_return_val_if_fail (style != NULL, 12.0);
1592 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SIZE), 12.0);
1594 return style->font_detail.size;
1597 void
1598 gnm_style_set_format (GnmStyle *style, GOFormat const *format)
1600 g_return_if_fail (style != NULL);
1601 g_return_if_fail (format != NULL);
1603 elem_changed (style, MSTYLE_FORMAT);
1604 go_format_ref (format);
1605 elem_clear_contents (style, MSTYLE_FORMAT);
1606 elem_set (style, MSTYLE_FORMAT);
1607 style->format = format;
1611 * gnm_style_set_format_text:
1613 * @style: mstyle to change.
1614 * @format: An *untranslated* format string.
1616 void
1617 gnm_style_set_format_text (GnmStyle *style, char const *format)
1619 GOFormat *sf;
1621 g_return_if_fail (style != NULL);
1622 g_return_if_fail (format != NULL);
1624 sf = go_format_new_from_XL (format);
1625 gnm_style_set_format (style, sf);
1626 go_format_unref (sf);
1629 const GOFormat *
1630 gnm_style_get_format (GnmStyle const *style)
1632 g_return_val_if_fail (style != NULL, NULL);
1633 g_return_val_if_fail (elem_is_set (style, MSTYLE_FORMAT), NULL);
1635 return style->format;
1638 void
1639 gnm_style_set_align_h (GnmStyle *style, GnmHAlign a)
1641 g_return_if_fail (style != NULL);
1643 elem_changed (style, MSTYLE_ALIGN_H);
1644 elem_set (style, MSTYLE_ALIGN_H);
1645 style->h_align = a;
1648 GnmHAlign
1649 gnm_style_get_align_h (GnmStyle const *style)
1651 g_return_val_if_fail (style != NULL, GNM_HALIGN_LEFT);
1652 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), GNM_HALIGN_LEFT);
1654 return style->h_align;
1657 void
1658 gnm_style_set_align_v (GnmStyle *style, GnmVAlign a)
1660 g_return_if_fail (style != NULL);
1662 elem_changed (style, MSTYLE_ALIGN_V);
1663 elem_set (style, MSTYLE_ALIGN_V);
1664 style->v_align = a;
1667 GnmVAlign
1668 gnm_style_get_align_v (GnmStyle const *style)
1670 g_return_val_if_fail (style != NULL, GNM_VALIGN_TOP);
1671 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), GNM_VALIGN_TOP);
1673 return style->v_align;
1676 void
1677 gnm_style_set_indent (GnmStyle *style, int i)
1679 g_return_if_fail (style != NULL);
1681 elem_changed (style, MSTYLE_INDENT);
1682 elem_set (style, MSTYLE_INDENT);
1683 style->indent = i;
1687 gnm_style_get_indent (GnmStyle const *style)
1689 g_return_val_if_fail (style != NULL, 0);
1690 g_return_val_if_fail (elem_is_set (style, MSTYLE_INDENT), 0);
1692 return style->indent;
1695 void
1696 gnm_style_set_rotation (GnmStyle *style, int rot_deg)
1698 g_return_if_fail (style != NULL);
1700 elem_changed (style, MSTYLE_ROTATION);
1701 elem_set (style, MSTYLE_ROTATION);
1702 style->rotation = rot_deg;
1706 gnm_style_get_rotation (GnmStyle const *style)
1708 g_return_val_if_fail (style != NULL, 0);
1709 g_return_val_if_fail (elem_is_set (style, MSTYLE_ROTATION), 0);
1711 return style->rotation;
1714 void
1715 gnm_style_set_text_dir (GnmStyle *style, GnmTextDir text_dir)
1717 g_return_if_fail (style != NULL);
1719 elem_changed (style, MSTYLE_TEXT_DIR);
1720 elem_set (style, MSTYLE_TEXT_DIR);
1721 style->text_dir = text_dir;
1724 GnmTextDir
1725 gnm_style_get_text_dir (GnmStyle const *style)
1727 g_return_val_if_fail (style != NULL, GNM_TEXT_DIR_CONTEXT);
1728 g_return_val_if_fail (elem_is_set (style, MSTYLE_TEXT_DIR), GNM_TEXT_DIR_CONTEXT);
1730 return style->text_dir;
1733 void
1734 gnm_style_set_wrap_text (GnmStyle *style, gboolean f)
1736 g_return_if_fail (style != NULL);
1738 elem_changed (style, MSTYLE_WRAP_TEXT);
1739 elem_set (style, MSTYLE_WRAP_TEXT);
1740 style->wrap_text = !!f;
1743 gboolean
1744 gnm_style_get_wrap_text (GnmStyle const *style)
1746 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1748 return style->wrap_text;
1752 * Same as gnm_style_get_wrap_text except that if either halign or valign
1753 * is _JUSTIFY, the result will be TRUE.
1755 gboolean
1756 gnm_style_get_effective_wrap_text (GnmStyle const *style)
1758 g_return_val_if_fail (style != NULL, FALSE);
1759 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1760 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), FALSE);
1761 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), FALSE);
1763 /* Note: GNM_HALIGN_GENERAL never expands to GNM_HALIGN_JUSTIFY. */
1764 return (style->wrap_text ||
1765 style->v_align == GNM_VALIGN_JUSTIFY ||
1766 style->v_align == GNM_VALIGN_DISTRIBUTED ||
1767 style->h_align == GNM_HALIGN_JUSTIFY);
1770 void
1771 gnm_style_set_shrink_to_fit (GnmStyle *style, gboolean f)
1773 g_return_if_fail (style != NULL);
1775 elem_changed (style, MSTYLE_SHRINK_TO_FIT);
1776 elem_set (style, MSTYLE_SHRINK_TO_FIT);
1777 style->shrink_to_fit = !!f;
1780 gboolean
1781 gnm_style_get_shrink_to_fit (GnmStyle const *style)
1783 g_return_val_if_fail (style != NULL, FALSE);
1784 g_return_val_if_fail (elem_is_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
1786 return style->shrink_to_fit;
1789 void
1790 gnm_style_set_contents_locked (GnmStyle *style, gboolean f)
1792 g_return_if_fail (style != NULL);
1794 elem_changed (style, MSTYLE_CONTENTS_LOCKED);
1795 elem_set (style, MSTYLE_CONTENTS_LOCKED);
1796 style->contents_locked = !!f;
1799 gboolean
1800 gnm_style_get_contents_locked (GnmStyle const *style)
1802 g_return_val_if_fail (style != NULL, FALSE);
1803 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_LOCKED), FALSE);
1805 return style->contents_locked;
1808 void
1809 gnm_style_set_contents_hidden (GnmStyle *style, gboolean f)
1811 g_return_if_fail (style != NULL);
1813 elem_changed (style, MSTYLE_CONTENTS_HIDDEN);
1814 elem_set (style, MSTYLE_CONTENTS_HIDDEN);
1815 style->contents_hidden = !!f;
1818 gboolean
1819 gnm_style_get_contents_hidden (GnmStyle const *style)
1821 g_return_val_if_fail (style != NULL, FALSE);
1822 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN), FALSE);
1824 return style->contents_hidden;
1828 * gnm_style_set_validation:
1829 * @style: #GnmStyle
1830 * @v: (transfer full): #GnmValidation
1832 void
1833 gnm_style_set_validation (GnmStyle *style, GnmValidation *v)
1835 g_return_if_fail (style != NULL);
1837 elem_clear_contents (style, MSTYLE_VALIDATION);
1838 elem_changed (style, MSTYLE_VALIDATION);
1839 elem_set (style, MSTYLE_VALIDATION);
1840 style->validation = v;
1844 * gnm_style_get_validation:
1845 * @style: #GnmStyle
1847 * Returns: (transfer none):
1849 GnmValidation const *
1850 gnm_style_get_validation (GnmStyle const *style)
1852 g_return_val_if_fail (style != NULL, NULL);
1853 g_return_val_if_fail (elem_is_set (style, MSTYLE_VALIDATION), NULL);
1855 return style->validation;
1859 * gnm_style_set_hlink:
1860 * @style: #GnmStyle
1861 * @lnk: (transfer full): #GnmHLink
1863 * This sets a link for @style.
1865 void
1866 gnm_style_set_hlink (GnmStyle *style, GnmHLink *lnk)
1868 g_return_if_fail (style != NULL);
1870 elem_clear_contents (style, MSTYLE_HLINK);
1871 elem_changed (style, MSTYLE_HLINK);
1872 elem_set (style, MSTYLE_HLINK);
1873 style->hlink = lnk;
1877 * gnm_style_get_hlink:
1878 * @style: #GnmStyle
1880 * Returns: (transfer none): the associated #GnmHLink.
1882 GnmHLink *
1883 gnm_style_get_hlink (GnmStyle const *style)
1885 g_return_val_if_fail (style != NULL, NULL);
1886 g_return_val_if_fail (elem_is_set (style, MSTYLE_HLINK), NULL);
1888 return style->hlink;
1892 * gnm_style_set_input_msg:
1893 * @style: #GnmStyle
1894 * @msg: (transfer full): #GnmInputMsg
1896 * This sets an input message for @style.
1898 void
1899 gnm_style_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
1901 g_return_if_fail (style != NULL);
1903 elem_clear_contents (style, MSTYLE_INPUT_MSG);
1904 elem_changed (style, MSTYLE_INPUT_MSG);
1905 elem_set (style, MSTYLE_INPUT_MSG);
1906 style->input_msg = msg;
1910 * gnm_style_get_input_msg:
1911 * @style: #GnmStyle
1913 * Returns: (transfer none): the currently set input message assuming
1914 * that the style has such.
1916 GnmInputMsg *
1917 gnm_style_get_input_msg (GnmStyle const *style)
1919 g_return_val_if_fail (style != NULL, NULL);
1920 g_return_val_if_fail (elem_is_set (style, MSTYLE_INPUT_MSG), NULL);
1922 return style->input_msg;
1926 * gnm_style_set_conditions:
1927 * @style: #GnmStyle
1928 * @sc: (transfer full): #GnmStyleConditions
1930 * This sets conditional style for @style.
1932 void
1933 gnm_style_set_conditions (GnmStyle *style, GnmStyleConditions *sc)
1935 g_return_if_fail (style != NULL);
1937 elem_clear_contents (style, MSTYLE_CONDITIONS);
1938 elem_changed (style, MSTYLE_CONDITIONS);
1939 elem_set (style, MSTYLE_CONDITIONS);
1940 style->conditions = sc;
1944 * gnm_style_get_conditions:
1945 * @style: #GnmStyle
1947 * Returns: (transfer none): the currently set conditional style assuming
1948 * that the style has such.
1950 GnmStyleConditions *
1951 gnm_style_get_conditions (GnmStyle const *style)
1953 g_return_val_if_fail (style != NULL, NULL);
1954 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
1955 return style->conditions;
1958 static gboolean
1959 debug_style_deps (void)
1961 static int debug = -1;
1962 if (debug < 0)
1963 debug = gnm_debug_flag ("style-deps");
1964 return debug;
1968 * Just a simple version for now. We can also ignore most function
1969 * calls[1] and self-references[2].
1971 * [1] Excluding volatile (TODAY, ...) and those that can create references
1972 * outside the arguments (INDIRECT).
1974 * [2] References that print like A1 when used in A1.
1976 static gboolean
1977 cond_expr_harmless (GnmExpr const *expr)
1979 GnmValue const *v = gnm_expr_get_constant (expr);
1980 if (v && !VALUE_IS_CELLRANGE (v))
1981 return TRUE;
1983 return FALSE;
1987 void
1988 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
1990 GnmStyleConditions *sc;
1991 Sheet *sheet;
1993 g_return_if_fail (style != NULL);
1994 g_return_if_fail (r != NULL);
1996 sheet = style->linked_sheet;
1999 * Conditional formatting.
2001 * We need to trigger a reformatting of the cell if a cell referenced
2002 * by the condition changes.
2004 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2005 ? gnm_style_get_conditions (style)
2006 : NULL;
2007 if (sc) {
2008 GPtrArray const *conds = gnm_style_conditions_details (sc);
2009 guint ui;
2010 if (debug_style_deps ())
2011 g_printerr ("Linking %s for %p\n",
2012 range_as_string (r), style);
2013 for (ui = 0; conds && ui < conds->len; ui++) {
2014 GnmStyleCond const *c = g_ptr_array_index (conds, ui);
2015 guint ei;
2017 for (ei = 0; ei < 2; ei++) {
2018 GnmExprTop const *texpr =
2019 gnm_style_cond_get_expr (c, ei);
2020 if (!texpr ||
2021 cond_expr_harmless (texpr->expr))
2022 continue;
2023 if (!style->deps)
2024 style->deps = g_ptr_array_new ();
2025 gnm_dep_style_dependency
2026 (sheet, texpr, r, style->deps);
2032 * Validations.
2034 * We can probably ignore those. If a dependent cell changes such
2035 * that a validation condition is no longer satisfied, it is
2036 * grandfathered in as valid.
2039 /* The style owns the deps. */
2042 void
2043 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2045 unsigned ui, k;
2047 g_return_if_fail (style != NULL);
2048 g_return_if_fail (r != NULL);
2050 if (!style->deps)
2051 return;
2053 for (ui = k = 0; ui < style->deps->len; ui++) {
2054 GnmDependent *dep = g_ptr_array_index (style->deps, ui);
2055 GnmCellPos const *pos = dependent_pos (dep);
2057 if (range_contains (r, pos->col, pos->row)) {
2058 if (debug_style_deps ())
2059 g_printerr ("Unlinking %s for %p\n",
2060 cellpos_as_string (pos), style);
2061 dependent_set_expr (dep, NULL);
2062 g_free (dep);
2063 } else {
2064 g_ptr_array_index (style->deps, k) = dep;
2065 k++;
2069 g_ptr_array_set_size (style->deps, k);
2074 * gnm_style_visible_in_blank:
2075 * @style: style to query
2077 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2078 * that means if it has a background or a visible border.
2080 gboolean
2081 gnm_style_visible_in_blank (GnmStyle const *style)
2083 GnmStyleElement i;
2085 g_return_val_if_fail (style != NULL, FALSE);
2087 if (elem_is_set (style, MSTYLE_PATTERN) &&
2088 gnm_style_get_pattern (style) > 0)
2089 return TRUE;
2091 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2092 if (elem_is_set (style, i) &&
2093 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2094 return TRUE;
2096 return FALSE;
2099 static void
2100 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2102 attr->start_index = 0;
2103 attr->end_index = G_MAXINT;
2104 pango_attr_list_insert (attrs, attr);
2108 * gnm_style_get_pango_attrs :
2109 * @style: #GnmStyle
2111 PangoAttrList *
2112 gnm_style_get_pango_attrs (GnmStyle const *style,
2113 PangoContext *context,
2114 double zoom)
2116 PangoAttrList *l;
2117 GnmUnderline ul;
2118 GnmFont *font = gnm_style_get_font (style, context);
2120 if (style->pango_attrs) {
2121 if (zoom == style->pango_attrs_zoom) {
2122 pango_attr_list_ref (style->pango_attrs);
2123 return style->pango_attrs;
2125 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2128 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2129 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2130 ((GnmStyle *)style)->pango_attrs_height = -1;
2132 /* Foreground colour. */
2133 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2134 if (0) {
2135 GnmColor const *fore = style->color.font;
2136 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2139 /* Handle underlining. */
2140 ul = gnm_style_get_font_uline (style);
2141 if (ul != UNDERLINE_NONE)
2142 add_attr (l,
2143 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2145 /* Handle strikethrough. */
2146 if (gnm_style_get_font_strike (style))
2147 add_attr (l, pango_attr_strikethrough_new (TRUE));
2149 /* Handle script. */
2150 switch (gnm_style_get_font_script (style)) {
2151 default :
2152 case GO_FONT_SCRIPT_STANDARD :
2153 break;
2154 case GO_FONT_SCRIPT_SUB :
2155 add_attr (l, go_pango_attr_subscript_new (TRUE));
2156 break;
2157 case GO_FONT_SCRIPT_SUPER :
2158 add_attr (l, go_pango_attr_superscript_new (TRUE));
2159 break;
2162 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2164 if (zoom != 1)
2165 add_attr (l, pango_attr_scale_new (zoom));
2167 pango_attr_list_ref (l);
2168 return l;
2171 PangoAttrList *
2172 gnm_style_generate_attrs_full (GnmStyle const *style)
2174 GnmColor const *fore = style->color.font;
2175 PangoAttrList *l = pango_attr_list_new ();
2177 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2178 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2179 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2180 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2181 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2182 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2183 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2184 add_attr (l, pango_attr_strikethrough_new
2185 (gnm_style_get_font_strike (style)));
2186 add_attr (l, pango_attr_underline_new
2187 (gnm_translate_underline_to_pango
2188 (gnm_style_get_font_uline (style))));
2189 return l;
2193 gnm_style_get_pango_height (GnmStyle const *style,
2194 PangoContext *context,
2195 double zoom)
2197 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2199 if (style->pango_attrs_height == -1) {
2200 int h;
2201 PangoLayout *layout = pango_layout_new (context);
2202 GOFormat const *fmt;
2203 gboolean requires_translation = FALSE;
2205 fmt = gnm_style_get_format (style);
2206 if (!go_format_is_general (fmt)) {
2207 GOFormatDetails details;
2208 go_format_get_details (fmt, &details, NULL);
2209 if (details.family == GO_FORMAT_SCIENTIFIC &&
2210 details.use_markup) {
2211 PangoAttribute *a
2212 = go_pango_attr_superscript_new (TRUE);
2213 /* We want to superscript the "-01" in the */
2214 /* string "+1.23456789E-01" */
2215 a->start_index = 12;
2216 a->end_index = 15;
2217 pango_attr_list_insert (attrs, a);
2218 requires_translation = TRUE;
2221 pango_layout_set_attributes (layout, attrs);
2222 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2223 if (requires_translation)
2224 go_pango_translate_layout (layout);
2225 pango_layout_get_pixel_size (layout, NULL, &h);
2226 g_object_unref (layout);
2227 ((GnmStyle *)style)->pango_attrs_height = h;
2230 pango_attr_list_unref (attrs);
2231 return style->pango_attrs_height;
2235 void
2236 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2238 switch (attr->klass->type) {
2239 case PANGO_ATTR_FAMILY :
2240 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2241 break;
2242 case PANGO_ATTR_SIZE :
2243 gnm_style_set_font_size (style,
2244 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2245 break;
2246 case PANGO_ATTR_STYLE :
2247 gnm_style_set_font_italic (style,
2248 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2249 break;
2250 case PANGO_ATTR_WEIGHT :
2251 gnm_style_set_font_bold (style,
2252 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2253 break;
2254 case PANGO_ATTR_FOREGROUND :
2255 gnm_style_set_font_color (style, gnm_color_new_pango (
2256 &((PangoAttrColor *)attr)->color));
2257 break;
2258 case PANGO_ATTR_UNDERLINE :
2259 gnm_style_set_font_uline
2260 (style, gnm_translate_underline_from_pango
2261 (((PangoAttrInt *)attr)->value));
2262 break;
2263 case PANGO_ATTR_STRIKETHROUGH :
2264 gnm_style_set_font_strike (style,
2265 ((PangoAttrInt *)attr)->value != 0);
2266 break;
2267 default : {
2268 gboolean script_seen = FALSE, script_set = FALSE;
2269 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2270 script_seen = TRUE;
2271 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2272 script_set = TRUE;
2273 gnm_style_set_font_script
2274 (style, GO_FONT_SCRIPT_SUPER);
2276 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2277 script_seen = TRUE;
2278 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2279 script_set = TRUE;
2280 gnm_style_set_font_script
2281 (style, GO_FONT_SCRIPT_SUB);
2284 if (script_seen && !script_set)
2285 gnm_style_set_font_script
2286 (style, GO_FONT_SCRIPT_STANDARD);
2287 break; /* ignored */
2292 /* ------------------------------------------------------------------------- */
2294 static void
2295 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2297 if (color)
2298 g_printerr ("\t%s: %x:%x:%x%s\n",
2299 gnm_style_element_name [elem],
2300 GO_COLOR_UINT_R (color->go_color),
2301 GO_COLOR_UINT_G (color->go_color),
2302 GO_COLOR_UINT_B (color->go_color),
2303 color->is_auto ? " auto" : "");
2304 else
2305 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2308 static void
2309 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2311 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2312 if (border)
2313 g_printerr ("%d\n", border->line_type);
2314 else
2315 g_printerr ("blank\n");
2319 * gnm_style_dump:
2320 * @style: style to dump
2322 * This function dumps the given style's contents to stderr. This is meant
2323 * for debug purposes only and doesn't do a very good job for, for example,
2324 * conditional style settings.
2326 void
2327 gnm_style_dump (GnmStyle const *style)
2329 int i;
2331 g_printerr ("Style Refs %d\n", style->ref_count);
2332 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2333 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2334 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2335 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2337 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2338 if (elem_is_set (style, i))
2339 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2341 if (elem_is_set (style, MSTYLE_PATTERN))
2342 g_printerr ("\tpattern %d\n", style->pattern);
2343 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2344 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2345 if (elem_is_set (style, MSTYLE_FONT_NAME))
2346 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2347 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2348 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2349 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2350 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2351 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2352 switch (style->font_detail.underline) {
2353 default :
2354 case UNDERLINE_NONE :
2355 g_printerr ("\tno underline\n"); break;
2356 case UNDERLINE_SINGLE :
2357 g_printerr ("\tsingle underline\n"); break;
2358 case UNDERLINE_DOUBLE :
2359 g_printerr ("\tdouble underline\n"); break;
2361 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2362 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2363 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2364 switch (style->font_detail.script) {
2365 case GO_FONT_SCRIPT_SUB :
2366 g_printerr ("\tsubscript\n"); break;
2367 default :
2368 case GO_FONT_SCRIPT_STANDARD :
2369 g_printerr ("\tno super or sub\n"); break;
2370 case GO_FONT_SCRIPT_SUPER :
2371 g_printerr ("\tsuperscript\n"); break;
2373 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2374 g_printerr ("\tsize %f\n", style->font_detail.size);
2375 if (elem_is_set (style, MSTYLE_FORMAT)) {
2376 const char *fmt = go_format_as_XL (style->format);
2377 g_printerr ("\tformat '%s'\n", fmt);
2379 if (elem_is_set (style, MSTYLE_ALIGN_V))
2380 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2381 if (elem_is_set (style, MSTYLE_ALIGN_H))
2382 g_printerr ("\thalign %hd\n", (short)style->h_align);
2383 if (elem_is_set (style, MSTYLE_INDENT))
2384 g_printerr ("\tindent %d\n", style->indent);
2385 if (elem_is_set (style, MSTYLE_ROTATION))
2386 g_printerr ("\trotation %d\n", style->rotation);
2387 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2388 g_printerr ("\ttext dir %d\n", style->text_dir);
2389 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2390 g_printerr ("\twrap text %d\n", style->wrap_text);
2391 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2392 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2393 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2394 g_printerr ("\tlocked %d\n", style->contents_locked);
2395 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2396 g_printerr ("\thidden %d\n", style->contents_hidden);
2397 if (elem_is_set (style, MSTYLE_VALIDATION))
2398 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2399 if (elem_is_set (style, MSTYLE_HLINK))
2400 g_printerr ("\thlink %p\n", (void *)style->hlink);
2401 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2402 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2403 if (elem_is_set (style, MSTYLE_CONDITIONS))
2404 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2407 /* ------------------------------------------------------------------------- */
2409 void
2410 gnm_style_init (void)
2412 #if USE_MSTYLE_POOL
2413 gnm_style_pool =
2414 go_mem_chunk_new ("style pool",
2415 sizeof (GnmStyle),
2416 16 * 1024 - 128);
2417 #endif
2420 #if USE_MSTYLE_POOL
2421 static void
2422 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2424 GnmStyle *style = data;
2425 g_printerr ("Leaking style at %p.\n", (void *)style);
2426 gnm_style_dump (style);
2428 #endif
2430 void
2431 gnm_style_shutdown (void)
2433 #if USE_MSTYLE_POOL
2434 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2435 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2436 gnm_style_pool = NULL;
2437 #endif