Introspection update
[gnumeric.git] / src / mstyle.c
blob02c2dac445f9332cdd476227a322fa3081c172b3
1 /*
2 * mstyle.c: Storing a style
4 * Authors:
5 * Michael Meeks <mmeeks@gnu.org>
6 * Almer S. Tigelaar <almer@gnome.org>
7 * Jody Goldberg <jody@gnome.org>
8 * Morten Welinder <terra@gnome.org>
9 */
10 #include <gnumeric-config.h>
11 #include "gnumeric.h"
12 #include "style.h"
14 #include "sheet-style.h"
15 #include "style-border.h"
16 #include "style-font.h"
17 #include "style-color.h"
18 #include "style-conditions.h"
19 #include "validation.h"
20 #include "pattern.h"
21 #include "hlink.h"
22 #include "input-msg.h"
23 #include "application.h"
24 #include "parse-util.h"
25 #include "expr.h"
26 #include "value.h"
27 #include "gutils.h"
28 #include "ranges.h"
29 #include "gnumeric-conf.h"
30 #include <goffice/goffice.h>
31 #include <string.h>
33 #define DEBUG_STYLES
34 #ifndef USE_MSTYLE_POOL
35 #define USE_MSTYLE_POOL 1
36 #endif
38 #if USE_MSTYLE_POOL
39 /* Memory pool for GnmStyles. */
40 static GOMemChunk *gnm_style_pool;
41 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
42 #define CHUNK_ALLOC0(T,p) ((T*)go_mem_chunk_alloc0 (p))
43 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
44 #else
45 #define CHUNK_ALLOC(T,c) g_new (T,1)
46 #define CHUNK_ALLOC0(T,c) g_new0 (T,1)
47 #define CHUNK_FREE(p,v) g_free ((v))
48 #endif
51 struct _GnmStyle {
52 unsigned int changed;
53 unsigned int set;
55 unsigned int hash_key;
56 unsigned int hash_key_xl;
57 unsigned int ref_count;
58 unsigned int link_count;
59 Sheet *linked_sheet;
61 PangoAttrList *pango_attrs;
62 double pango_attrs_zoom;
63 int pango_attrs_height;
65 GnmFont *font;
66 PangoContext *font_context;
68 /* public */
69 struct _GnmStyleColor {
70 GnmColor *font;
71 GnmColor *back;
72 GnmColor *pattern;
73 } color;
74 GnmBorder *borders[MSTYLE_BORDER_DIAGONAL - MSTYLE_BORDER_TOP + 1];
75 guint32 pattern;
77 /* FIXME: TODO use GOFont */
78 struct _GnmStyleFontDetails {
79 GOString *name;
80 gboolean bold;
81 gboolean italic;
82 GnmUnderline underline;
83 gboolean strikethrough;
84 GOFontScript script;
85 double size;
86 } font_detail;
88 GOFormat const *format;
89 GnmHAlign h_align;
90 GnmVAlign v_align;
91 int indent;
92 int rotation;
93 int text_dir;
94 gboolean wrap_text;
95 gboolean shrink_to_fit;
96 gboolean contents_locked;
97 gboolean contents_hidden;
99 GnmValidation *validation;
100 GnmHLink *hlink;
101 GnmInputMsg *input_msg;
102 GnmStyleConditions *conditions;
103 GPtrArray *cond_styles;
105 GPtrArray *deps;
108 #define elem_changed(style, elem) do { (style)->changed |= (1u << (elem)); } while(0)
109 #define elem_set(style, elem) do { (style)->set |= (1u << (elem)); } while(0)
110 #define elem_unset(style, elem) do { (style)->set &= ~(1u << (elem)); } while(0)
111 #define elem_is_set(style, elem) (((style)->set & (1u << (elem))) != 0)
113 #define MSTYLE_ANY_BORDER MSTYLE_BORDER_TOP: \
114 case MSTYLE_BORDER_BOTTOM: \
115 case MSTYLE_BORDER_LEFT: \
116 case MSTYLE_BORDER_RIGHT: \
117 case MSTYLE_BORDER_DIAGONAL: \
118 case MSTYLE_BORDER_REV_DIAGONAL
121 #define UNROLLED_FOR(init_,cond_,step_,code_) \
122 do { \
123 init_; \
124 if (cond_) { code_; step_; \
125 if (cond_) { code_; step_; \
126 if (cond_) { code_; step_; \
127 if (cond_) { code_; step_; \
128 if (cond_) { code_; step_; \
129 if (cond_) { code_; step_; \
130 if (cond_) { code_; step_; \
131 if (cond_) { code_; step_; \
132 if (cond_) { code_; step_; \
133 if (cond_) { code_; step_; \
134 if (cond_) { code_; step_; \
135 if (cond_) { code_; step_; \
136 if (cond_) { code_; step_; \
137 if (cond_) { code_; step_; \
138 if (cond_) { code_; step_; \
139 if (cond_) { code_; step_; \
140 if (cond_) { code_; step_; \
141 if (cond_) { code_; step_; \
142 if (cond_) { code_; step_; \
143 if (cond_) { code_; step_; \
144 if (cond_) { code_; step_; \
145 if (cond_) { code_; step_; \
146 if (cond_) { code_; step_; \
147 if (cond_) { code_; step_; \
148 if (cond_) { code_; step_; \
149 if (cond_) { code_; step_; \
150 if (cond_) { code_; step_; \
151 if (cond_) { code_; step_; \
152 if (cond_) { code_; step_; \
153 if (cond_) { code_; step_; \
154 if (cond_) { code_; step_; \
155 if (cond_) { code_; step_; \
156 if (cond_) { code_; step_; \
157 if (cond_) { code_; step_; \
158 if (cond_) { code_; step_; \
159 if (cond_) { code_; step_; \
160 if (cond_) { code_; step_; \
161 if (cond_) { code_; step_; \
162 if (cond_) { code_; step_; \
163 if (cond_) { code_; step_; \
164 if (cond_) { code_; step_; \
165 if (cond_) { code_; step_; \
166 if (cond_) { code_; step_; \
167 if (cond_) { code_; step_; \
168 if (cond_) { code_; step_; \
169 if (cond_) { code_; step_; \
170 if (cond_) { code_; step_; \
171 if (cond_) { code_; step_; \
172 if (cond_) { code_; step_; \
173 if (cond_) { code_; step_; \
174 g_assert_not_reached (); \
175 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} \
176 } while (0)
180 static char const * const
181 gnm_style_element_name[MSTYLE_ELEMENT_MAX] = {
182 "Color.Back",
183 "Color.Pattern",
184 "Border.Top",
185 "Border.Bottom",
186 "Border.Left",
187 "Border.Right",
188 "Border.RevDiagonal",
189 "Border.Diagonal",
190 "Pattern",
191 "Color.Fore",
192 "Font.Name",
193 "Font.Bold",
194 "Font.Italic",
195 "Font.Underline",
196 "Font.Strikethrough",
197 "Font.Script",
198 "Font.Size",
199 "Format",
200 "Align.v",
201 "Align.h",
202 "Indent",
203 "Rotation",
204 "WrapText",
205 "ShrinkToFit",
206 "Contents.Locked",
207 "Contents.Hidden",
208 "Validation",
209 "Hyper Link",
210 "Input Msg"
213 /* Some ref/link count debugging */
214 #if 0
215 #define d(arg) g_printerr arg
216 #else
217 #define d(arg) do { } while (0)
218 #endif
220 static void
221 clear_conditional_merges (GnmStyle *style)
223 if (style->cond_styles) {
224 unsigned i = style->cond_styles->len;
225 while (i-- > 0)
226 gnm_style_unref (g_ptr_array_index (style->cond_styles, i));
227 g_ptr_array_free (style->cond_styles, TRUE);
228 style->cond_styles = NULL;
232 #define MIX(H) do { \
233 H *= G_GUINT64_CONSTANT(123456789012345); \
234 H ^= (H >> 31); \
235 } while (0)
237 static void
238 gnm_style_update (GnmStyle *style)
240 guint64 hash = 0;
241 int i;
243 g_return_if_fail (style->changed);
245 style->changed = 0;
247 clear_conditional_merges (style);
248 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
249 style->cond_styles = gnm_style_conditions_overlay (style->conditions, style);
251 /* ---------------------------------------- */
253 if (elem_is_set (style, MSTYLE_COLOR_BACK)) {
254 if (!style->color.back->is_auto)
255 hash ^= GPOINTER_TO_UINT (style->color.back);
256 else
257 hash++;
259 MIX (hash);
261 if (elem_is_set (style, MSTYLE_COLOR_PATTERN)) {
262 if (!style->color.pattern->is_auto)
263 hash ^= GPOINTER_TO_UINT (style->color.pattern);
264 else
265 hash++;
267 MIX (hash);
269 if (elem_is_set (style, MSTYLE_FONT_COLOR)) {
270 if (!style->color.font->is_auto)
271 hash ^= GPOINTER_TO_UINT (style->color.font);
272 else
273 hash++;
275 MIX (hash);
277 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
278 if (elem_is_set (style, i))
279 hash ^= GPOINTER_TO_UINT (style->borders[i - MSTYLE_BORDER_TOP]);
280 else
281 hash++;
282 MIX (hash);
285 if (elem_is_set (style, MSTYLE_PATTERN))
286 hash ^= style->pattern;
287 MIX (hash);
289 if (elem_is_set (style, MSTYLE_FONT_NAME))
290 hash ^= GPOINTER_TO_UINT (style->font_detail.name);
291 MIX (hash);
293 if (elem_is_set (style, MSTYLE_FONT_BOLD))
294 hash ^= (style->font_detail.bold ? 1 : 2);
295 MIX (hash);
297 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
298 hash ^= (style->font_detail.italic ? 1 : 2);
299 MIX (hash);
301 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
302 hash ^= (style->font_detail.underline ? 1 : 2);
303 MIX (hash);
305 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
306 hash ^= (style->font_detail.strikethrough ? 1 : 2);
307 MIX (hash);
309 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
310 hash ^= (style->font_detail.script + 0x100);
311 MIX (hash);
313 if (elem_is_set (style, MSTYLE_FONT_SIZE))
314 hash ^= ((int)(style->font_detail.size * 97));
315 MIX (hash);
317 if (elem_is_set (style, MSTYLE_FORMAT))
318 hash ^= GPOINTER_TO_UINT (style->format);
319 MIX (hash);
321 if (elem_is_set (style, MSTYLE_ALIGN_H))
322 hash ^= (style->h_align + 0x100);
323 MIX (hash);
325 if (elem_is_set (style, MSTYLE_ALIGN_V))
326 hash ^= (style->v_align + 0x100);
327 MIX (hash);
329 if (elem_is_set (style, MSTYLE_INDENT))
330 hash ^= style->indent;
331 MIX (hash);
333 if (elem_is_set (style, MSTYLE_ROTATION))
334 hash ^= style->rotation;
335 MIX (hash);
337 if (elem_is_set (style, MSTYLE_TEXT_DIR))
338 hash ^= (style->text_dir + 0x100);
339 MIX (hash);
341 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
342 hash ^= (style->wrap_text ? 1 : 2);
343 MIX (hash);
345 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
346 hash ^= (style->shrink_to_fit ? 1 : 2);
347 MIX (hash);
349 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
350 hash ^= (style->contents_locked ? 1 : 2);
351 MIX (hash);
353 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
354 hash ^= (style->contents_hidden ? 1 : 2);
355 MIX (hash);
357 style->hash_key_xl = (guint32)hash;
359 /* From here on, fields are not in MS XL */
361 if (elem_is_set (style, MSTYLE_VALIDATION)) {
363 * The hash used must not depend on the expressions inside
364 * the validation.
366 hash ^= (style->validation != NULL ? 1 : 2);
368 MIX (hash);
370 if (elem_is_set (style, MSTYLE_HLINK))
371 hash ^= GPOINTER_TO_UINT (style->hlink);
372 MIX (hash);
374 if (elem_is_set (style, MSTYLE_INPUT_MSG))
375 hash ^= GPOINTER_TO_UINT (style->input_msg);
376 MIX (hash);
378 if (elem_is_set (style, MSTYLE_CONDITIONS)) {
380 * The hash used must not depend on the expressions inside
381 * the conditions.
383 hash ^= style->conditions
384 ? gnm_style_conditions_hash (style->conditions)
385 : 1u;
387 MIX (hash);
389 style->hash_key = (guint32)hash;
391 if (G_UNLIKELY (style->set == 0)) {
393 * gnm_style_new and gnm_style_dup both assume that the
394 * correct hash values (both of them) for the empty style
395 * is zero.
397 g_assert (style->hash_key == 0);
398 g_assert (style->hash_key_xl == 0);
402 #undef MIX
404 guint
405 gnm_style_hash_XL (gconstpointer style)
407 if (((GnmStyle const *)style)->changed)
408 gnm_style_update ((GnmStyle *)style);
409 return ((GnmStyle const *)style)->hash_key_xl;
412 guint
413 gnm_style_hash (gconstpointer style)
415 if (((GnmStyle const *)style)->changed)
416 gnm_style_update ((GnmStyle *)style);
417 return ((GnmStyle const *)style)->hash_key;
420 #define ELEM_IS_EQ(a,b,elem) \
421 (elem == MSTYLE_COLOR_BACK \
422 ? a->color.back == b->color.back || (a->color.back->is_auto && b->color.back->is_auto) \
423 : (elem == MSTYLE_COLOR_PATTERN \
424 ? a->color.pattern == b->color.pattern || (a->color.pattern->is_auto && b->color.pattern->is_auto) \
425 : (elem >= MSTYLE_BORDER_TOP && elem <= MSTYLE_BORDER_DIAGONAL) \
426 ? a->borders[elem - MSTYLE_BORDER_TOP] == b->borders[elem - MSTYLE_BORDER_TOP] \
427 : (elem == MSTYLE_PATTERN \
428 ? a->pattern == b->pattern \
429 : (elem == MSTYLE_FONT_COLOR \
430 ? a->color.font == b->color.font || (a->color.font->is_auto && b->color.font->is_auto) \
431 : (elem == MSTYLE_FONT_NAME \
432 ? a->font_detail.name == b->font_detail.name \
433 : (elem == MSTYLE_FONT_BOLD \
434 ? a->font_detail.bold == b->font_detail.bold \
435 : (elem == MSTYLE_FONT_ITALIC \
436 ? a->font_detail.italic == b->font_detail.italic \
437 : (elem == MSTYLE_FONT_UNDERLINE \
438 ? a->font_detail.underline == b->font_detail.underline \
439 : (elem == MSTYLE_FONT_STRIKETHROUGH \
440 ? a->font_detail.strikethrough == b->font_detail.strikethrough \
441 : (elem == MSTYLE_FONT_SCRIPT \
442 ? a->font_detail.script == b->font_detail.script \
443 : (elem == MSTYLE_FONT_SIZE \
444 ? a->font_detail.size == b->font_detail.size \
445 : (elem == MSTYLE_FORMAT \
446 ? a->format == b->format \
447 : (elem == MSTYLE_ALIGN_V \
448 ? a->v_align == b->v_align \
449 : (elem == MSTYLE_ALIGN_H \
450 ? a->h_align == b->h_align \
451 : (elem == MSTYLE_INDENT \
452 ? a->indent == b->indent \
453 : (elem == MSTYLE_ROTATION \
454 ? a->rotation == b->rotation \
455 : (elem == MSTYLE_TEXT_DIR \
456 ? a->text_dir == b->text_dir \
457 : (elem == MSTYLE_WRAP_TEXT \
458 ? a->wrap_text == b->wrap_text \
459 : (elem == MSTYLE_SHRINK_TO_FIT \
460 ? a->shrink_to_fit == b->shrink_to_fit \
461 : (elem == MSTYLE_CONTENTS_LOCKED \
462 ? a->contents_locked == b->contents_locked \
463 : (elem == MSTYLE_CONTENTS_HIDDEN \
464 ? a->contents_hidden == b->contents_hidden \
465 : (elem == MSTYLE_VALIDATION \
466 ? a->validation == b->validation \
467 : (elem == MSTYLE_HLINK \
468 ? a->hlink == b->hlink \
469 : (elem == MSTYLE_INPUT_MSG \
470 ? a->input_msg == b->input_msg \
471 : (elem == MSTYLE_CONDITIONS \
472 ? (a->conditions == b->conditions || \
473 (a->conditions && b->conditions && \
474 gnm_style_conditions_equal (a->conditions, b->conditions, FALSE))) \
475 : FALSE)))))))))))))))))))))))))
478 * Note: the above is suboptimal for validation, hlink, input_msg.
480 * We are comparing pointers (which at least safely matches what we do
481 * with the hash), but I think we want proper equality.
484 static gboolean
485 elem_is_eq (GnmStyle const *a, GnmStyle const *b, GnmStyleElement elem)
487 return ELEM_IS_EQ (a, b, elem);
490 static void
491 elem_assign_contents (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
493 #ifdef DEBUG_STYLES
494 g_return_if_fail (src != dst);
495 g_return_if_fail (elem_is_set (src, elem));
496 #endif
497 switch (elem) {
498 case MSTYLE_COLOR_BACK : style_color_ref (dst->color.back = src->color.back); return;
499 case MSTYLE_COLOR_PATTERN : style_color_ref (dst->color.pattern = src->color.pattern); return;
500 case MSTYLE_ANY_BORDER:
501 elem -= MSTYLE_BORDER_TOP;
502 gnm_style_border_ref (dst->borders[elem] = src->borders[elem]);
503 return;
504 case MSTYLE_PATTERN: dst->pattern = src->pattern; return;
505 case MSTYLE_FONT_COLOR : style_color_ref (dst->color.font = src->color.font); return;
506 case MSTYLE_FONT_NAME: go_string_ref (dst->font_detail.name = src->font_detail.name); return;
507 case MSTYLE_FONT_BOLD: dst->font_detail.bold = src->font_detail.bold; return;
508 case MSTYLE_FONT_ITALIC: dst->font_detail.italic = src->font_detail.italic; return;
509 case MSTYLE_FONT_UNDERLINE: dst->font_detail.underline = src->font_detail.underline; return;
510 case MSTYLE_FONT_STRIKETHROUGH: dst->font_detail.strikethrough = src->font_detail.strikethrough; return;
511 case MSTYLE_FONT_SCRIPT: dst->font_detail.script = src->font_detail.script; return;
512 case MSTYLE_FONT_SIZE: dst->font_detail.size = src->font_detail.size; return;
513 case MSTYLE_FORMAT: go_format_ref (dst->format = src->format); return;
514 case MSTYLE_ALIGN_V: dst->v_align = src->v_align; return;
515 case MSTYLE_ALIGN_H: dst->h_align = src->h_align; return;
516 case MSTYLE_INDENT: dst->indent = src->indent; return;
517 case MSTYLE_ROTATION: dst->rotation = src->rotation; return;
518 case MSTYLE_TEXT_DIR: dst->text_dir = src->text_dir; return;
519 case MSTYLE_WRAP_TEXT: dst->wrap_text = src->wrap_text; return;
520 case MSTYLE_SHRINK_TO_FIT: dst->shrink_to_fit = src->shrink_to_fit; return;
521 case MSTYLE_CONTENTS_LOCKED: dst->contents_locked = src->contents_locked; return;
522 case MSTYLE_CONTENTS_HIDDEN: dst->contents_hidden = src->contents_hidden; return;
523 case MSTYLE_VALIDATION:
524 if ((dst->validation = src->validation))
525 gnm_validation_ref (dst->validation);
526 return;
527 case MSTYLE_HLINK:
528 if ((dst->hlink = src->hlink))
529 g_object_ref (dst->hlink);
530 return;
531 case MSTYLE_INPUT_MSG:
532 if ((dst->input_msg = src->input_msg))
533 g_object_ref (dst->input_msg);
534 return;
535 case MSTYLE_CONDITIONS:
536 if ((dst->conditions = src->conditions))
537 g_object_ref (dst->conditions);
538 return;
539 default:
544 static void
545 elem_clear_contents (GnmStyle *style, GnmStyleElement elem)
547 #ifdef DEBUG_STYLES
548 g_return_if_fail (style != NULL);
549 #endif
550 if (!elem_is_set (style, elem))
551 return;
553 switch (elem) {
554 case MSTYLE_COLOR_BACK : style_color_unref (style->color.back); return;
555 case MSTYLE_COLOR_PATTERN : style_color_unref (style->color.pattern); return;
556 case MSTYLE_ANY_BORDER:
557 gnm_style_border_unref (style->borders[elem - MSTYLE_BORDER_TOP]);
558 return;
559 case MSTYLE_FONT_COLOR : style_color_unref (style->color.font); return;
560 case MSTYLE_FONT_NAME: go_string_unref (style->font_detail.name); return;
561 case MSTYLE_FORMAT: go_format_unref (style->format); return;
562 case MSTYLE_VALIDATION:
563 if (style->validation)
564 gnm_validation_unref (style->validation);
565 return;
566 case MSTYLE_HLINK:
567 if (style->hlink)
568 g_object_unref (style->hlink);
569 return;
570 case MSTYLE_INPUT_MSG:
571 if (style->input_msg)
572 g_object_unref (style->input_msg);
573 return;
574 case MSTYLE_CONDITIONS:
575 if (style->conditions) {
576 clear_conditional_merges (style);
577 g_object_unref (style->conditions);
579 return;
580 default:
586 * gnm_style_find_conflicts :
587 * @accum: accumulator #GnmStyle
588 * @overlay: #GnmStyle
589 * @conflicts: flags
591 * Copy any items from @overlay that do not conflict with the values in @accum.
592 * If an element had a previous conflict (flagged via @conflicts) it is ignored.
594 * Returns @conflicts with any new conflicts added.
596 unsigned int
597 gnm_style_find_conflicts (GnmStyle *accum, GnmStyle const *overlay,
598 unsigned int conflicts)
600 int i;
602 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (conflicts));
604 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
605 if (conflicts & (1u << i) || !elem_is_set (overlay, i)) {
606 /* Nothing */
607 } else if (!elem_is_set (accum, i)) {
608 elem_assign_contents (accum, overlay, i);
609 elem_set (accum, i);
610 elem_changed (accum, i);
611 } else if (!elem_is_eq (accum, overlay, i))
612 conflicts |= (1u << i);
615 return conflicts;
618 #define GNM_INPUT_MSG_EQUAL3(a,b,r) (gnm_input_msg_equal (a,b))
620 #define RELAX_CHECK(op_,field_,checker_) do { \
621 if (diffs & (1u << (op_)) && \
622 elem_is_set (a, (op_)) && \
623 elem_is_set (b, (op_)) && \
624 ((a->field_ == NULL) != (b->field_ == NULL) || \
625 checker_ (a->field_, b->field_, relax_sheet))) \
626 diffs &= ~(1u << (op_)); \
627 } while (0)
630 * gnm_style_find_differences:
631 * @a: A #GnmStyle
632 * @b: A #GnmStyle
633 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
635 * Determine how two fully-qualified styles differ.
637 * Returns differences as a bitset of #GnmStyleElement.
639 unsigned int
640 gnm_style_find_differences (GnmStyle const *a, GnmStyle const *b,
641 gboolean relax_sheet)
643 int i;
644 unsigned int diffs = 0;
646 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (diffs));
648 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
649 if (elem_is_set (a, i) != elem_is_set (b, i) ||
650 (elem_is_set (a, i) && !elem_is_eq (a, b, i)))
651 diffs |= (1u << i);
654 if (relax_sheet) {
655 RELAX_CHECK (MSTYLE_HLINK, hlink, gnm_hlink_equal);
656 RELAX_CHECK (MSTYLE_VALIDATION, validation, gnm_validation_equal);
657 RELAX_CHECK (MSTYLE_INPUT_MSG, input_msg, GNM_INPUT_MSG_EQUAL3);
658 RELAX_CHECK (MSTYLE_CONDITIONS, conditions, gnm_style_conditions_equal);
661 return diffs;
664 #undef RELAX_CHECK
665 #undef GNM_INPUT_MSG_EQUAL3
667 static inline void
668 gnm_style_clear_pango (GnmStyle *style)
670 if (style->pango_attrs) {
671 pango_attr_list_unref (style->pango_attrs);
672 style->pango_attrs = NULL;
677 static inline void
678 gnm_style_clear_font (GnmStyle *style)
680 if (style->font) {
681 gnm_font_unref (style->font);
682 style->font = NULL;
684 g_clear_object (&style->font_context);
688 * gnm_style_new:
690 * Returns: (transfer full): a new style with _no_ elements set.
692 GnmStyle *
693 gnm_style_new (void)
695 GnmStyle *style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
697 style->ref_count = 1;
698 style->link_count = 0;
699 style->linked_sheet = NULL;
700 style->pango_attrs = NULL;
701 style->font = NULL;
702 style->validation = NULL;
704 style->set = style->changed = 0;
705 style->validation = NULL;
706 style->hlink = NULL;
707 style->input_msg = NULL;
708 style->conditions = NULL;
710 d(("new %p\n", style));
712 return style;
716 * gnm_style_new_default:
718 * Returns: (transfer full): a new style initialized to the default state.
720 GnmStyle *
721 gnm_style_new_default (void)
723 GnmStyle *new_style = gnm_style_new ();
724 int i;
726 gnm_style_set_font_name (new_style, gnm_conf_get_core_defaultfont_name ());
727 gnm_style_set_font_size (new_style, gnm_conf_get_core_defaultfont_size ());
728 gnm_style_set_font_bold (new_style, gnm_conf_get_core_defaultfont_bold ());
729 gnm_style_set_font_italic (new_style, gnm_conf_get_core_defaultfont_italic ());
731 gnm_style_set_format (new_style, go_format_general ());
732 gnm_style_set_align_v (new_style, GNM_VALIGN_BOTTOM);
733 gnm_style_set_align_h (new_style, GNM_HALIGN_GENERAL);
734 gnm_style_set_indent (new_style, 0);
735 gnm_style_set_rotation (new_style, 0);
736 gnm_style_set_text_dir (new_style, GNM_TEXT_DIR_CONTEXT);
737 gnm_style_set_wrap_text (new_style, FALSE);
738 gnm_style_set_shrink_to_fit (new_style, FALSE);
739 gnm_style_set_contents_locked (new_style, TRUE);
740 gnm_style_set_contents_hidden (new_style, FALSE);
741 gnm_style_set_font_uline (new_style, UNDERLINE_NONE);
742 gnm_style_set_font_strike (new_style, FALSE);
743 gnm_style_set_font_script (new_style, GO_FONT_SCRIPT_STANDARD);
745 gnm_style_set_validation (new_style, NULL);
746 gnm_style_set_hlink (new_style, NULL);
747 gnm_style_set_input_msg (new_style, NULL);
748 gnm_style_set_conditions (new_style, NULL);
750 gnm_style_set_font_color (new_style, style_color_black ());
751 gnm_style_set_back_color (new_style, style_color_auto_back ());
752 gnm_style_set_pattern_color (new_style, style_color_black ());
754 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
755 gnm_style_set_border (new_style, i,
756 gnm_style_border_ref (gnm_style_border_none ()));
757 gnm_style_set_pattern (new_style, 0);
759 return new_style;
762 GnmStyle *
763 gnm_style_dup (GnmStyle const *src)
765 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
766 int i;
768 new_style->ref_count = 1;
769 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
770 if (elem_is_set (src, i)) {
771 elem_assign_contents (new_style, src, i);
772 elem_set (new_style, i);
773 elem_changed (new_style, i);
776 if ((new_style->pango_attrs = src->pango_attrs)) {
777 pango_attr_list_ref (new_style->pango_attrs);
778 new_style->pango_attrs_zoom = src->pango_attrs_zoom;
781 if ((new_style->font = src->font)) {
782 gnm_font_ref (new_style->font);
783 new_style->font_context = g_object_ref (src->font_context);
786 d(("dup %p\n", new_style));
787 return new_style;
791 * gnm_style_new_merged :
792 * @base: #GnmStyle
793 * @overlay: #GnmStyle
795 * A new GnmStyle that contains any elements of @overlay that are set, and uses
796 * @base for anything that is not set in @overlay.
798 * Returns: (transfer full): A ref to a new GnmStyle.
800 GnmStyle *
801 gnm_style_new_merged (GnmStyle const *base, GnmStyle const *overlay)
803 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
804 int i;
806 new_style->ref_count = 1;
807 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
808 if (elem_is_set (overlay, i))
809 elem_assign_contents (new_style, overlay, i);
810 else if (elem_is_set (base, i))
811 elem_assign_contents (new_style, base, i);
812 else
813 continue;
814 elem_set (new_style, i);
815 elem_changed (new_style, i);
817 d(("copy merge %p\n", new_style));
818 return new_style;
822 * gnm_style_ref: (skip)
823 * @style: #GnmStyle
825 * Returns: (transfer full): A new reference to @style.
827 GnmStyle *
828 gnm_style_ref (GnmStyle const *style)
830 g_return_val_if_fail (style != NULL, NULL);
831 g_return_val_if_fail (style->ref_count > 0, NULL);
833 ((GnmStyle *)style)->ref_count++;
834 d(("ref %p = %d\n", style, style->ref_count));
836 return ((GnmStyle *)style);
840 * gnm_style_unref: (skip)
841 * @style: #GnmStyle const
843 * Unrefs and _potentially frees_ @style.
844 * Takes a _const_ pointer to facilitate life cycles. The const indicates that
845 * the content can not be changed, mainly when handling styles that are in the
846 * style hash.
848 void
849 gnm_style_unref (GnmStyle const *style)
851 g_return_if_fail (style != NULL);
852 g_return_if_fail (style->ref_count > 0);
854 d(("unref %p = %d\n", style, style->ref_count-1));
855 if (((GnmStyle *)style)->ref_count-- <= 1) {
856 GnmStyle *unconst = (GnmStyle *)style;
857 int i;
859 g_return_if_fail (style->link_count == 0);
860 g_return_if_fail (style->linked_sheet == NULL);
862 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
863 elem_clear_contents (unconst, i);
864 unconst->set = 0;
865 clear_conditional_merges (unconst);
866 gnm_style_clear_pango (unconst);
867 gnm_style_clear_font (unconst);
869 if (style->deps) {
870 if (style->deps->len > 0)
871 g_warning ("Leftover style deps!");
872 g_ptr_array_free (style->deps, TRUE);
875 CHUNK_FREE (gnm_style_pool, unconst);
879 GType
880 gnm_style_get_type (void)
882 static GType t = 0;
884 if (t == 0) {
885 t = g_boxed_type_register_static ("GnmStyle",
886 (GBoxedCopyFunc)gnm_style_ref,
887 (GBoxedFreeFunc)gnm_style_unref);
889 return t;
893 * Replace auto pattern color in style with sheet's auto pattern color.
894 * make_copy tells if we are allowed to modify the style in place or we must
895 * make a copy first.
897 static GnmStyle *
898 link_pattern_color (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
900 GnmColor *pattern_color = style->color.pattern;
902 if (pattern_color->is_auto && auto_color != pattern_color) {
903 style_color_ref (auto_color);
904 if (make_copy) {
905 GnmStyle *orig = style;
906 style = gnm_style_dup (style);
907 gnm_style_unref (orig);
909 gnm_style_set_pattern_color (style, auto_color);
911 return style;
915 * Replace auto border colors in style with sheet's auto pattern
916 * color. (pattern is *not* a typo.)
917 * make_copy tells if we are allowed to modify the style in place or we must
918 * make a copy first.
920 * FIXME: We conjecture that XL color 64 in border should change with the
921 * pattern, but not color 127. That distinction is not yet represented in
922 * our data structures.
924 static GnmStyle *
925 link_border_colors (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
927 int i;
929 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i) {
930 if (elem_is_set (style, i)) {
931 GnmBorder *border =
932 style->borders[i- MSTYLE_BORDER_TOP];
933 GnmColor *color;
935 if (!border)
936 continue;
938 color = border->color;
939 if (color->is_auto && auto_color != color) {
940 GnmBorder *new_border;
941 GnmStyleBorderOrientation orientation;
943 switch (i) {
944 case MSTYLE_BORDER_LEFT:
945 case MSTYLE_BORDER_RIGHT:
946 orientation = GNM_STYLE_BORDER_VERTICAL;
947 break;
948 case MSTYLE_BORDER_REV_DIAGONAL:
949 case MSTYLE_BORDER_DIAGONAL:
950 orientation = GNM_STYLE_BORDER_DIAGONAL;
951 break;
952 case MSTYLE_BORDER_TOP:
953 case MSTYLE_BORDER_BOTTOM:
954 default:
955 orientation = GNM_STYLE_BORDER_HORIZONTAL;
956 break;
958 style_color_ref (auto_color);
959 new_border = gnm_style_border_fetch (
960 border->line_type, auto_color,
961 orientation);
963 if (make_copy) {
964 GnmStyle *orig = style;
965 style = gnm_style_dup (style);
966 gnm_style_unref (orig);
967 make_copy = FALSE;
969 gnm_style_set_border (style, i, new_border);
973 return style;
976 static void
977 gnm_style_linked_sheet_changed (GnmStyle *style)
979 Sheet *sheet = style->linked_sheet;
981 if (elem_is_set (style, MSTYLE_VALIDATION) &&
982 style->validation &&
983 gnm_validation_get_sheet (style->validation) != sheet) {
984 GnmValidation *new_v = gnm_validation_dup (style->validation);
985 gnm_validation_set_sheet (new_v, sheet);
986 gnm_style_set_validation (style, new_v);
989 if (elem_is_set (style, MSTYLE_HLINK) &&
990 style->hlink &&
991 gnm_hlink_get_sheet (style->hlink) != sheet) {
992 GnmHLink *new_l = gnm_hlink_dup (style->hlink);
993 gnm_hlink_set_sheet (new_l, sheet);
994 gnm_style_set_hlink (style, new_l);
997 if (elem_is_set (style, MSTYLE_CONDITIONS) &&
998 style->conditions &&
999 gnm_style_conditions_get_sheet (style->conditions) != sheet) {
1000 GnmStyleConditions *new_c = gnm_style_conditions_dup (style->conditions);
1001 gnm_style_conditions_set_sheet (new_c, sheet);
1002 gnm_style_set_conditions (style, new_c);
1007 * gnm_style_link_sheet :
1008 * @style:
1009 * @sheet:
1011 * ABSORBS a reference to the style and sets the link count to 1.
1013 * Where auto pattern color occurs in the style (it may for pattern and
1014 * borders), it is replaced with the sheet's auto pattern color. We make
1015 * sure that we do not modify the style which was passed in to us, but also
1016 * that we don't copy more than once. The final argument to the
1017 * link_xxxxx_color functions tell whether or not to copy.
1019 GnmStyle *
1020 gnm_style_link_sheet (GnmStyle *style, Sheet *sheet)
1022 GnmColor *auto_color;
1023 gboolean style_is_orig = TRUE;
1025 if (style->linked_sheet != NULL) {
1026 GnmStyle *orig = style;
1027 style = gnm_style_dup (style);
1028 gnm_style_unref (orig);
1029 style_is_orig = FALSE;
1031 /* safety test */
1032 g_return_val_if_fail (style->linked_sheet != sheet, style);
1035 g_return_val_if_fail (style->link_count == 0, style);
1036 g_return_val_if_fail (style->linked_sheet == NULL, style);
1038 auto_color = sheet_style_get_auto_pattern_color (sheet);
1039 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1040 style = link_pattern_color (style, auto_color, style_is_orig);
1041 style = link_border_colors (style, auto_color, style_is_orig);
1042 style_color_unref (auto_color);
1044 style->linked_sheet = sheet;
1045 style->link_count = 1;
1047 gnm_style_linked_sheet_changed (style);
1049 d(("link sheet %p = 1\n", style));
1050 return style;
1053 void
1054 gnm_style_link (GnmStyle *style)
1056 g_return_if_fail (style->link_count > 0);
1058 style->link_count++;
1059 d(("link %p = %d\n", style, style->link_count));
1062 void
1063 gnm_style_link_multiple (GnmStyle *style, int count)
1065 g_return_if_fail (style->link_count > 0);
1067 style->link_count += count;
1068 d(("multiple link %p + %d = %d\n", style, count, style->link_count));
1071 void
1072 gnm_style_unlink (GnmStyle *style)
1074 g_return_if_fail (style->link_count > 0);
1076 d(("unlink %p = %d\n", style, style->link_count-1));
1077 if (style->link_count-- == 1) {
1078 sheet_style_unlink (style->linked_sheet, style);
1079 style->linked_sheet = NULL;
1080 gnm_style_unref (style);
1084 // Internal function for sheet-style.c use only
1085 void
1086 gnm_style_abandon_link (GnmStyle *style)
1088 style->link_count = 0;
1089 style->linked_sheet = NULL;
1092 gboolean
1093 gnm_style_eq (GnmStyle const *a, GnmStyle const *b)
1095 return a == b;
1098 gboolean
1099 gnm_style_equal (GnmStyle const *a, GnmStyle const *b)
1101 int i;
1103 if (a == b)
1104 return TRUE;
1105 if (a->set != b->set || !gnm_style_equal_XL (a, b))
1106 return FALSE;
1107 UNROLLED_FOR (i = MSTYLE_VALIDATION, i < MSTYLE_ELEMENT_MAX, i++, {
1108 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1109 return FALSE;
1112 return TRUE;
1115 gboolean
1116 gnm_style_equal_XL (GnmStyle const *a, GnmStyle const *b)
1118 int i;
1120 g_return_val_if_fail (a != NULL, FALSE);
1121 g_return_val_if_fail (b != NULL, FALSE);
1123 if (a == b)
1124 return TRUE;
1126 if ((a->set ^ b->set) & ((1u << MSTYLE_VALIDATION) - 1))
1127 return FALSE;
1129 UNROLLED_FOR (i = MSTYLE_COLOR_BACK, i < MSTYLE_VALIDATION, i++, {
1130 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1131 return FALSE;
1133 return TRUE;
1137 * gnm_style_equal_elem:
1138 * @a: first style
1139 * @b: second style
1140 * @e: style element
1142 * Returns: %TRUE, if the two styles have the same contents for the
1143 * given element, either because neither have it set, or because both
1144 * have it set and to the same value.
1146 gboolean
1147 gnm_style_equal_elem (GnmStyle const *a, GnmStyle const *b, GnmStyleElement e)
1149 if (elem_is_set (a, e))
1150 return elem_is_set (b, e) && elem_is_eq (a, b, e);
1151 else
1152 return !elem_is_set (b, e);
1157 #define CMP_TRY_NUMBER_RAW(a_,b_) \
1158 do { \
1159 if ((a_) < (b_)) return -1; \
1160 if ((a_) > (b_)) return -1; \
1161 } while (0)
1163 #define CMP_TRY_NUMBER(e_,f_) \
1164 do { \
1165 if (elem_is_set (a, (e_))) \
1166 CMP_TRY_NUMBER_RAW(a->f_, b->f_); \
1167 } while (0)
1169 #define CMP_TRY_COLOR(e_,f_) \
1170 do { \
1171 if (elem_is_set (a, (e_))) { \
1172 CMP_TRY_NUMBER_RAW(a->f_->is_auto, b->f_->is_auto); \
1173 CMP_TRY_NUMBER_RAW(a->f_->go_color, b->f_->go_color); \
1175 } while (0)
1178 * Ordering of GnmStyles. Apart from FIXMEs, this shouldn't change
1179 * from one run to the next.
1182 gnm_style_cmp (GnmStyle const *a, GnmStyle const *b)
1184 GnmStyleElement e;
1186 if (a == b)
1187 return 0;
1190 * Very quick comparison based on what is set. This also allows
1191 * us to check on one elem_is_set below.
1193 CMP_TRY_NUMBER_RAW (a->set, b->set);
1195 CMP_TRY_COLOR (MSTYLE_FONT_COLOR, color.font);
1196 CMP_TRY_COLOR (MSTYLE_COLOR_BACK, color.back);
1197 CMP_TRY_COLOR (MSTYLE_COLOR_PATTERN, color.pattern);
1198 for (e = MSTYLE_BORDER_TOP; e <= MSTYLE_BORDER_DIAGONAL; e++) {
1199 GnmBorder const *ba, *bb;
1200 if (!elem_is_set (a, e))
1201 continue;
1202 ba = a->borders[e - MSTYLE_BORDER_TOP];
1203 bb = b->borders[e - MSTYLE_BORDER_TOP];
1204 if (ba == bb)
1205 continue; /* Handles both being NULL */
1206 CMP_TRY_NUMBER_RAW(!!ba, !!bb);
1207 CMP_TRY_NUMBER_RAW(ba->line_type, bb->line_type);
1208 CMP_TRY_NUMBER_RAW(ba->color->go_color, bb->color->go_color);
1209 CMP_TRY_NUMBER_RAW(ba->begin_margin, bb->begin_margin);
1210 CMP_TRY_NUMBER_RAW(ba->end_margin, bb->end_margin);
1211 CMP_TRY_NUMBER_RAW(ba->width, bb->width);
1213 CMP_TRY_NUMBER (MSTYLE_PATTERN, pattern);
1214 if (elem_is_set (a, MSTYLE_FONT_NAME)) {
1215 /* Plain strcmp, not utf-8. We need to see diffs. */
1216 int tmp = strcmp (a->font_detail.name->str,
1217 b->font_detail.name->str);
1218 if (tmp)
1219 return tmp;
1221 CMP_TRY_NUMBER (MSTYLE_FONT_BOLD, font_detail.bold);
1222 CMP_TRY_NUMBER (MSTYLE_FONT_ITALIC, font_detail.italic);
1223 CMP_TRY_NUMBER (MSTYLE_FONT_UNDERLINE, font_detail.underline);
1224 CMP_TRY_NUMBER (MSTYLE_FONT_STRIKETHROUGH, font_detail.strikethrough);
1225 CMP_TRY_NUMBER (MSTYLE_FONT_SCRIPT, font_detail.script);
1226 CMP_TRY_NUMBER (MSTYLE_FONT_SIZE, font_detail.size);
1227 if (elem_is_set (a, MSTYLE_FORMAT)) {
1228 /* Plain strcmp, not utf-8. We need to see diffs. */
1229 int tmp = strcmp (go_format_as_XL (a->format),
1230 go_format_as_XL (b->format));
1231 if (tmp)
1232 return tmp;
1234 CMP_TRY_NUMBER (MSTYLE_ALIGN_H, h_align);
1235 CMP_TRY_NUMBER (MSTYLE_ALIGN_V, v_align);
1236 CMP_TRY_NUMBER (MSTYLE_INDENT, indent);
1237 CMP_TRY_NUMBER (MSTYLE_ROTATION, rotation);
1238 CMP_TRY_NUMBER (MSTYLE_TEXT_DIR, text_dir);
1239 CMP_TRY_NUMBER (MSTYLE_WRAP_TEXT, wrap_text);
1240 CMP_TRY_NUMBER (MSTYLE_SHRINK_TO_FIT, shrink_to_fit);
1241 CMP_TRY_NUMBER (MSTYLE_CONTENTS_LOCKED, contents_locked);
1242 CMP_TRY_NUMBER (MSTYLE_CONTENTS_HIDDEN, contents_hidden);
1243 /* FIXME: validation */
1244 /* FIXME: hlink */
1245 /* FIXME: input_msg */
1246 /* FIXME: conditions */
1247 /* FIXME: cond_styles */
1249 /* Last resort: pointer comparison. */
1250 return a < b ? -1 : +1;
1253 #undef CMP_TRY_NUMBER
1254 #undef CMP_TRY_COLOR
1258 * gnm_style_equal_header :
1259 * @a: #GnmStyle
1260 * @b: #GnmStyle
1261 * @top: is this a header vertically or horizontally
1263 * Check to see if @a is different enough from @b to make us think that @a is
1264 * from a header.
1266 gboolean
1267 gnm_style_equal_header (GnmStyle const *a, GnmStyle const *b, gboolean top)
1269 int i = top ? MSTYLE_BORDER_BOTTOM : MSTYLE_BORDER_RIGHT;
1271 if (!elem_is_eq (a, b, i))
1272 return FALSE;
1273 for (i = MSTYLE_COLOR_BACK; i <= MSTYLE_COLOR_PATTERN ; i++)
1274 if (!elem_is_eq (a, b, i))
1275 return FALSE;
1276 for (i = MSTYLE_FONT_COLOR; i <= MSTYLE_SHRINK_TO_FIT ; i++)
1277 if (!elem_is_eq (a, b, i))
1278 return FALSE;
1279 return TRUE;
1283 gboolean
1284 gnm_style_is_element_set (GnmStyle const *style, GnmStyleElement elem)
1286 g_return_val_if_fail (style != NULL, FALSE);
1287 g_return_val_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX, FALSE);
1288 return elem_is_set (style, elem);
1292 * gnm_style_is_complete :
1293 * @style: #GnmStyle
1295 * Returns TRUE if all elements are set.
1297 gboolean
1298 gnm_style_is_complete (GnmStyle const *style)
1300 g_return_val_if_fail (style != NULL, FALSE);
1302 return style->set == ((1u << MSTYLE_ELEMENT_MAX) - 1);
1305 void
1306 gnm_style_unset_element (GnmStyle *style, GnmStyleElement elem)
1308 g_return_if_fail (style != NULL);
1309 g_return_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX);
1311 if (elem_is_set (style, elem)) {
1312 elem_clear_contents (style, elem);
1313 elem_unset (style, elem);
1318 * gnm_style_merge :
1319 * @base: #GnmStyle
1320 * @overlay: #GnmStyle
1322 * Applies all active elements of @overlay onto @base.
1324 void
1325 gnm_style_merge (GnmStyle *base, GnmStyle const *overlay)
1327 unsigned i;
1328 if (base == overlay)
1329 return;
1330 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
1331 if (elem_is_set (overlay, i)) {
1332 elem_clear_contents (base, i);
1333 elem_assign_contents (base, overlay, i);
1334 elem_changed (base, i);
1339 * gnm_style_merge_element:
1340 * @dst: Destination style
1341 * @src: Source style
1342 * @elem: Element to replace
1344 * This function replaces element @elem in style @dst with element @elem
1345 * in style @src. (If element @elem was already set in style @dst then
1346 * the element will first be unset)
1348 void
1349 gnm_style_merge_element (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
1351 g_return_if_fail (src != NULL);
1352 g_return_if_fail (dst != NULL);
1353 g_return_if_fail (src != dst);
1355 if (elem_is_set (src, elem)) {
1356 elem_clear_contents (dst, elem);
1357 elem_assign_contents (dst, src, elem);
1358 elem_set (dst, elem);
1359 elem_changed (dst, elem);
1363 void
1364 gnm_style_set_font_color (GnmStyle *style, GnmColor *col)
1366 g_return_if_fail (style != NULL);
1367 g_return_if_fail (col != NULL);
1369 elem_changed (style, MSTYLE_FONT_COLOR);
1370 if (elem_is_set (style, MSTYLE_FONT_COLOR))
1371 style_color_unref (style->color.font);
1372 else
1373 elem_set (style, MSTYLE_FONT_COLOR);
1374 elem_changed (style, MSTYLE_FONT_COLOR);
1375 style->color.font = col;
1376 gnm_style_clear_pango (style);
1380 * gnm_style_set_back_color :
1381 * @style: #GnmStyle
1382 * @col: #GnmColor
1384 * Assigns @col as the background of @style.
1386 * NOTE : the background colour is only visibile if
1387 * GnmStyle::pattern > 0
1389 void
1390 gnm_style_set_back_color (GnmStyle *style, GnmColor *col)
1392 g_return_if_fail (style != NULL);
1393 g_return_if_fail (col != NULL);
1395 elem_changed (style, MSTYLE_COLOR_BACK);
1396 if (elem_is_set (style, MSTYLE_COLOR_BACK))
1397 style_color_unref (style->color.back);
1398 else
1399 elem_set (style, MSTYLE_COLOR_BACK);
1400 style->color.back = col;
1401 gnm_style_clear_pango (style);
1403 void
1404 gnm_style_set_pattern_color (GnmStyle *style, GnmColor *col)
1406 g_return_if_fail (style != NULL);
1407 g_return_if_fail (col != NULL);
1409 elem_changed (style, MSTYLE_COLOR_PATTERN);
1410 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1411 style_color_unref (style->color.pattern);
1412 else
1413 elem_set (style, MSTYLE_COLOR_PATTERN);
1414 style->color.pattern = col;
1415 gnm_style_clear_pango (style);
1418 GnmColor *
1419 gnm_style_get_font_color (GnmStyle const *style)
1421 g_return_val_if_fail (style != NULL, NULL);
1422 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_COLOR), NULL);
1423 return style->color.font;
1426 GnmColor *
1427 gnm_style_get_back_color (GnmStyle const *style)
1429 g_return_val_if_fail (style != NULL, NULL);
1430 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_BACK), NULL);
1431 return style->color.back;
1434 GnmColor *
1435 gnm_style_get_pattern_color (GnmStyle const *style)
1437 g_return_val_if_fail (style != NULL, NULL);
1438 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_PATTERN), NULL);
1439 return style->color.pattern;
1442 void
1443 gnm_style_set_border (GnmStyle *style, GnmStyleElement elem,
1444 GnmBorder *border)
1446 g_return_if_fail (style != NULL);
1448 /* NOTE : It is legal for border to be NULL */
1449 switch (elem) {
1450 case MSTYLE_ANY_BORDER:
1451 elem_changed (style, elem);
1452 elem_set (style, elem);
1453 elem -= MSTYLE_BORDER_TOP;
1454 if (style->borders[elem])
1455 gnm_style_border_unref (style->borders[elem]);
1456 style->borders[elem] = border;
1457 break;
1458 default:
1459 g_warning ("Not a border element");
1460 break;
1464 GnmBorder *
1465 gnm_style_get_border (GnmStyle const *style, GnmStyleElement elem)
1467 g_return_val_if_fail (style != NULL, NULL);
1469 switch (elem) {
1470 case MSTYLE_ANY_BORDER:
1471 return style->borders[elem - MSTYLE_BORDER_TOP ];
1473 default:
1474 g_warning ("Not a border element");
1475 return NULL;
1479 void
1480 gnm_style_set_pattern (GnmStyle *style, int pattern)
1482 g_return_if_fail (style != NULL);
1483 g_return_if_fail (pattern >= 0);
1484 g_return_if_fail (pattern < GNM_PATTERNS_MAX);
1486 elem_changed (style, MSTYLE_PATTERN);
1487 elem_set (style, MSTYLE_PATTERN);
1488 style->pattern = pattern;
1492 gnm_style_get_pattern (GnmStyle const *style)
1494 g_return_val_if_fail (style != NULL, 0);
1495 g_return_val_if_fail (elem_is_set (style, MSTYLE_PATTERN), 0);
1497 return style->pattern;
1501 * gnm_style_get_font:
1502 * @style: #GnmStyle
1503 * @context: #PangoContext
1505 * Returns: (transfer none):
1507 GnmFont *
1508 gnm_style_get_font (GnmStyle const *style, PangoContext *context)
1510 g_return_val_if_fail (style != NULL, NULL);
1512 if (!style->font || style->font_context != context) {
1513 char const *name;
1514 gboolean bold, italic;
1515 double size;
1517 gnm_style_clear_font ((GnmStyle *)style);
1519 if (elem_is_set (style, MSTYLE_FONT_NAME))
1520 name = gnm_style_get_font_name (style);
1521 else
1522 name = DEFAULT_FONT;
1524 if (elem_is_set (style, MSTYLE_FONT_BOLD))
1525 bold = gnm_style_get_font_bold (style);
1526 else
1527 bold = FALSE;
1529 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
1530 italic = gnm_style_get_font_italic (style);
1531 else
1532 italic = FALSE;
1534 if (elem_is_set (style, MSTYLE_FONT_SIZE))
1535 size = gnm_style_get_font_size (style);
1536 else
1537 size = DEFAULT_SIZE;
1539 ((GnmStyle *)style)->font =
1540 gnm_font_new (context, name, size, bold, italic);
1541 ((GnmStyle *)style)->font_context = g_object_ref (context);
1544 return style->font;
1548 * gnm_style_set_font_name:
1549 * @style: the style to change
1550 * @name: the font name as a string
1553 void
1554 gnm_style_set_font_name (GnmStyle *style, char const *name)
1556 g_return_if_fail (name != NULL);
1557 g_return_if_fail (style != NULL);
1559 elem_changed (style, MSTYLE_FONT_NAME);
1560 if (elem_is_set (style, MSTYLE_FONT_NAME))
1561 go_string_unref (style->font_detail.name);
1562 else
1563 elem_set (style, MSTYLE_FONT_NAME);
1564 style->font_detail.name = go_string_new (name);
1565 gnm_style_clear_font (style);
1566 gnm_style_clear_pango (style);
1570 * gnm_style_get_font_name:
1571 * @style: the style to query
1573 * Returns: (transfer none): the currently set font name
1575 char const *
1576 gnm_style_get_font_name (GnmStyle const *style)
1578 g_return_val_if_fail (style != NULL, NULL);
1579 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_NAME), NULL);
1581 return style->font_detail.name->str;
1584 void
1585 gnm_style_set_font_bold (GnmStyle *style, gboolean bold)
1587 g_return_if_fail (style != NULL);
1589 elem_changed (style, MSTYLE_FONT_BOLD);
1590 elem_set (style, MSTYLE_FONT_BOLD);
1591 style->font_detail.bold = !!bold;
1592 gnm_style_clear_font (style);
1593 gnm_style_clear_pango (style);
1597 * gnm_style_get_font_bold:
1598 * @style: #GnmStyle to query
1600 * Returns: %TRUE if the style has a bold font.
1602 gboolean
1603 gnm_style_get_font_bold (GnmStyle const *style)
1605 g_return_val_if_fail (style != NULL, FALSE);
1606 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_BOLD), FALSE);
1608 return style->font_detail.bold;
1611 void
1612 gnm_style_set_font_italic (GnmStyle *style, gboolean italic)
1614 g_return_if_fail (style != NULL);
1616 elem_changed (style, MSTYLE_FONT_ITALIC);
1617 elem_set (style, MSTYLE_FONT_ITALIC);
1618 style->font_detail.italic = !!italic;
1619 gnm_style_clear_font (style);
1620 gnm_style_clear_pango (style);
1623 gboolean
1624 gnm_style_get_font_italic (GnmStyle const *style)
1626 g_return_val_if_fail (style != NULL, FALSE);
1627 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_ITALIC), FALSE);
1629 return style->font_detail.italic;
1632 void
1633 gnm_style_set_font_uline (GnmStyle *style, GnmUnderline const underline)
1635 g_return_if_fail (style != NULL);
1636 g_return_if_fail (underline >= UNDERLINE_NONE && underline <= UNDERLINE_DOUBLE_LOW);
1638 elem_changed (style, MSTYLE_FONT_UNDERLINE);
1639 elem_set (style, MSTYLE_FONT_UNDERLINE);
1640 style->font_detail.underline = underline;
1641 gnm_style_clear_pango (style);
1644 GnmUnderline
1645 gnm_style_get_font_uline (GnmStyle const *style)
1647 g_return_val_if_fail (style != NULL, UNDERLINE_NONE);
1648 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_UNDERLINE), UNDERLINE_NONE);
1650 return style->font_detail.underline;
1653 void
1654 gnm_style_set_font_strike (GnmStyle *style, gboolean strikethrough)
1656 g_return_if_fail (style != NULL);
1658 elem_changed (style, MSTYLE_FONT_STRIKETHROUGH);
1659 elem_set (style, MSTYLE_FONT_STRIKETHROUGH);
1660 style->font_detail.strikethrough = !!strikethrough;
1661 gnm_style_clear_pango (style);
1664 gboolean
1665 gnm_style_get_font_strike (GnmStyle const *style)
1667 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
1669 return style->font_detail.strikethrough;
1672 void
1673 gnm_style_set_font_script (GnmStyle *style, GOFontScript script)
1675 g_return_if_fail (style != NULL);
1676 elem_changed (style, MSTYLE_FONT_SCRIPT);
1677 elem_set (style, MSTYLE_FONT_SCRIPT);
1678 style->font_detail.script = script;
1679 gnm_style_clear_pango (style);
1682 GOFontScript
1683 gnm_style_get_font_script (GnmStyle const *style)
1685 g_return_val_if_fail (style != NULL, GO_FONT_SCRIPT_STANDARD);
1686 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SCRIPT), GO_FONT_SCRIPT_STANDARD);
1688 return style->font_detail.script;
1691 void
1692 gnm_style_set_font_size (GnmStyle *style, double size)
1694 g_return_if_fail (style != NULL);
1695 g_return_if_fail (size >= 1.);
1696 elem_changed (style, MSTYLE_FONT_SIZE);
1697 elem_set (style, MSTYLE_FONT_SIZE);
1698 style->font_detail.size = size;
1699 gnm_style_clear_font (style);
1700 gnm_style_clear_pango (style);
1703 double
1704 gnm_style_get_font_size (GnmStyle const *style)
1706 g_return_val_if_fail (style != NULL, 12.0);
1707 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SIZE), 12.0);
1709 return style->font_detail.size;
1712 void
1713 gnm_style_set_format (GnmStyle *style, GOFormat const *format)
1715 g_return_if_fail (style != NULL);
1716 g_return_if_fail (format != NULL);
1718 elem_changed (style, MSTYLE_FORMAT);
1719 go_format_ref (format);
1720 elem_clear_contents (style, MSTYLE_FORMAT);
1721 elem_set (style, MSTYLE_FORMAT);
1722 style->format = format;
1726 * gnm_style_set_format_text:
1728 * @style: mstyle to change.
1729 * @format: An *untranslated* format string.
1731 void
1732 gnm_style_set_format_text (GnmStyle *style, char const *format)
1734 GOFormat *sf;
1736 g_return_if_fail (style != NULL);
1737 g_return_if_fail (format != NULL);
1739 sf = go_format_new_from_XL (format);
1740 gnm_style_set_format (style, sf);
1741 go_format_unref (sf);
1744 const GOFormat *
1745 gnm_style_get_format (GnmStyle const *style)
1747 g_return_val_if_fail (style != NULL, NULL);
1748 g_return_val_if_fail (elem_is_set (style, MSTYLE_FORMAT), NULL);
1750 return style->format;
1753 void
1754 gnm_style_set_align_h (GnmStyle *style, GnmHAlign a)
1756 g_return_if_fail (style != NULL);
1758 elem_changed (style, MSTYLE_ALIGN_H);
1759 elem_set (style, MSTYLE_ALIGN_H);
1760 style->h_align = a;
1763 GnmHAlign
1764 gnm_style_get_align_h (GnmStyle const *style)
1766 g_return_val_if_fail (style != NULL, GNM_HALIGN_LEFT);
1767 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), GNM_HALIGN_LEFT);
1769 return style->h_align;
1772 void
1773 gnm_style_set_align_v (GnmStyle *style, GnmVAlign a)
1775 g_return_if_fail (style != NULL);
1777 elem_changed (style, MSTYLE_ALIGN_V);
1778 elem_set (style, MSTYLE_ALIGN_V);
1779 style->v_align = a;
1782 GnmVAlign
1783 gnm_style_get_align_v (GnmStyle const *style)
1785 g_return_val_if_fail (style != NULL, GNM_VALIGN_TOP);
1786 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), GNM_VALIGN_TOP);
1788 return style->v_align;
1791 void
1792 gnm_style_set_indent (GnmStyle *style, int i)
1794 g_return_if_fail (style != NULL);
1796 elem_changed (style, MSTYLE_INDENT);
1797 elem_set (style, MSTYLE_INDENT);
1798 style->indent = i;
1802 gnm_style_get_indent (GnmStyle const *style)
1804 g_return_val_if_fail (style != NULL, 0);
1805 g_return_val_if_fail (elem_is_set (style, MSTYLE_INDENT), 0);
1807 return style->indent;
1810 void
1811 gnm_style_set_rotation (GnmStyle *style, int rot_deg)
1813 g_return_if_fail (style != NULL);
1815 elem_changed (style, MSTYLE_ROTATION);
1816 elem_set (style, MSTYLE_ROTATION);
1817 style->rotation = rot_deg;
1821 gnm_style_get_rotation (GnmStyle const *style)
1823 g_return_val_if_fail (style != NULL, 0);
1824 g_return_val_if_fail (elem_is_set (style, MSTYLE_ROTATION), 0);
1826 return style->rotation;
1829 void
1830 gnm_style_set_text_dir (GnmStyle *style, GnmTextDir text_dir)
1832 g_return_if_fail (style != NULL);
1834 elem_changed (style, MSTYLE_TEXT_DIR);
1835 elem_set (style, MSTYLE_TEXT_DIR);
1836 style->text_dir = text_dir;
1839 GnmTextDir
1840 gnm_style_get_text_dir (GnmStyle const *style)
1842 g_return_val_if_fail (style != NULL, GNM_TEXT_DIR_CONTEXT);
1843 g_return_val_if_fail (elem_is_set (style, MSTYLE_TEXT_DIR), GNM_TEXT_DIR_CONTEXT);
1845 return style->text_dir;
1848 void
1849 gnm_style_set_wrap_text (GnmStyle *style, gboolean f)
1851 g_return_if_fail (style != NULL);
1853 elem_changed (style, MSTYLE_WRAP_TEXT);
1854 elem_set (style, MSTYLE_WRAP_TEXT);
1855 style->wrap_text = !!f;
1858 gboolean
1859 gnm_style_get_wrap_text (GnmStyle const *style)
1861 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1863 return style->wrap_text;
1867 * Same as gnm_style_get_wrap_text except that if either halign or valign
1868 * is _JUSTIFY, the result will be TRUE.
1870 gboolean
1871 gnm_style_get_effective_wrap_text (GnmStyle const *style)
1873 g_return_val_if_fail (style != NULL, FALSE);
1874 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
1875 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), FALSE);
1876 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), FALSE);
1878 /* Note: GNM_HALIGN_GENERAL never expands to GNM_HALIGN_JUSTIFY. */
1879 return (style->wrap_text ||
1880 style->v_align == GNM_VALIGN_JUSTIFY ||
1881 style->v_align == GNM_VALIGN_DISTRIBUTED ||
1882 style->h_align == GNM_HALIGN_JUSTIFY);
1885 void
1886 gnm_style_set_shrink_to_fit (GnmStyle *style, gboolean f)
1888 g_return_if_fail (style != NULL);
1890 elem_changed (style, MSTYLE_SHRINK_TO_FIT);
1891 elem_set (style, MSTYLE_SHRINK_TO_FIT);
1892 style->shrink_to_fit = !!f;
1895 gboolean
1896 gnm_style_get_shrink_to_fit (GnmStyle const *style)
1898 g_return_val_if_fail (style != NULL, FALSE);
1899 g_return_val_if_fail (elem_is_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
1901 return style->shrink_to_fit;
1904 void
1905 gnm_style_set_contents_locked (GnmStyle *style, gboolean f)
1907 g_return_if_fail (style != NULL);
1909 elem_changed (style, MSTYLE_CONTENTS_LOCKED);
1910 elem_set (style, MSTYLE_CONTENTS_LOCKED);
1911 style->contents_locked = !!f;
1914 gboolean
1915 gnm_style_get_contents_locked (GnmStyle const *style)
1917 g_return_val_if_fail (style != NULL, FALSE);
1918 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_LOCKED), FALSE);
1920 return style->contents_locked;
1923 void
1924 gnm_style_set_contents_hidden (GnmStyle *style, gboolean f)
1926 g_return_if_fail (style != NULL);
1928 elem_changed (style, MSTYLE_CONTENTS_HIDDEN);
1929 elem_set (style, MSTYLE_CONTENTS_HIDDEN);
1930 style->contents_hidden = !!f;
1933 gboolean
1934 gnm_style_get_contents_hidden (GnmStyle const *style)
1936 g_return_val_if_fail (style != NULL, FALSE);
1937 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN), FALSE);
1939 return style->contents_hidden;
1943 * gnm_style_set_validation:
1944 * @style: #GnmStyle
1945 * @v: (transfer full): #GnmValidation
1947 void
1948 gnm_style_set_validation (GnmStyle *style, GnmValidation *v)
1950 g_return_if_fail (style != NULL);
1952 elem_clear_contents (style, MSTYLE_VALIDATION);
1953 elem_changed (style, MSTYLE_VALIDATION);
1954 elem_set (style, MSTYLE_VALIDATION);
1955 style->validation = v;
1959 * gnm_style_get_validation:
1960 * @style: #GnmStyle
1962 * Returns: (transfer none):
1964 GnmValidation const *
1965 gnm_style_get_validation (GnmStyle const *style)
1967 g_return_val_if_fail (style != NULL, NULL);
1968 g_return_val_if_fail (elem_is_set (style, MSTYLE_VALIDATION), NULL);
1970 return style->validation;
1974 * gnm_style_set_hlink:
1975 * @style: #GnmStyle
1976 * @lnk: (transfer full): #GnmHLink
1978 * This sets a link for @style.
1980 void
1981 gnm_style_set_hlink (GnmStyle *style, GnmHLink *lnk)
1983 g_return_if_fail (style != NULL);
1985 elem_clear_contents (style, MSTYLE_HLINK);
1986 elem_changed (style, MSTYLE_HLINK);
1987 elem_set (style, MSTYLE_HLINK);
1988 style->hlink = lnk;
1992 * gnm_style_get_hlink:
1993 * @style: #GnmStyle
1995 * Returns: (transfer none): the associated #GnmHLink.
1997 GnmHLink *
1998 gnm_style_get_hlink (GnmStyle const *style)
2000 g_return_val_if_fail (style != NULL, NULL);
2001 g_return_val_if_fail (elem_is_set (style, MSTYLE_HLINK), NULL);
2003 return style->hlink;
2007 * gnm_style_set_input_msg:
2008 * @style: #GnmStyle
2009 * @msg: (transfer full): #GnmInputMsg
2011 * This sets an input message for @style.
2013 void
2014 gnm_style_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
2016 g_return_if_fail (style != NULL);
2018 elem_clear_contents (style, MSTYLE_INPUT_MSG);
2019 elem_changed (style, MSTYLE_INPUT_MSG);
2020 elem_set (style, MSTYLE_INPUT_MSG);
2021 style->input_msg = msg;
2025 * gnm_style_get_input_msg:
2026 * @style: #GnmStyle
2028 * Returns: (transfer none): the currently set input message assuming
2029 * that the style has such.
2031 GnmInputMsg *
2032 gnm_style_get_input_msg (GnmStyle const *style)
2034 g_return_val_if_fail (style != NULL, NULL);
2035 g_return_val_if_fail (elem_is_set (style, MSTYLE_INPUT_MSG), NULL);
2037 return style->input_msg;
2041 * gnm_style_set_conditions:
2042 * @style: #GnmStyle
2043 * @sc: (transfer full): #GnmStyleConditions
2045 * This sets conditional style for @style.
2047 void
2048 gnm_style_set_conditions (GnmStyle *style, GnmStyleConditions *sc)
2050 g_return_if_fail (style != NULL);
2052 elem_clear_contents (style, MSTYLE_CONDITIONS);
2053 elem_changed (style, MSTYLE_CONDITIONS);
2054 elem_set (style, MSTYLE_CONDITIONS);
2055 style->conditions = sc;
2059 * gnm_style_get_conditions:
2060 * @style: #GnmStyle
2062 * Returns: (transfer none): the currently set conditional style assuming
2063 * that the style has such.
2065 GnmStyleConditions *
2066 gnm_style_get_conditions (GnmStyle const *style)
2068 g_return_val_if_fail (style != NULL, NULL);
2069 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2070 return style->conditions;
2074 * gnm_style_get_cond_style:
2075 * @style: #GnmStyle
2076 * @ix: The index of the condition for which style is desired
2078 * Returns: (transfer none): the resulting style from applying the condition's
2079 * style overlay onto @style.
2081 GnmStyle const *
2082 gnm_style_get_cond_style (GnmStyle const *style, int ix)
2084 g_return_val_if_fail (style != NULL, NULL);
2085 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2086 g_return_val_if_fail (style->cond_styles != NULL, NULL);
2088 g_return_val_if_fail (ix >= 0 && (unsigned)ix < style->cond_styles->len, NULL);
2090 return g_ptr_array_index (style->cond_styles, ix);
2095 static gboolean
2096 debug_style_deps (void)
2098 static int debug = -1;
2099 if (debug < 0)
2100 debug = gnm_debug_flag ("style-deps");
2101 return debug;
2105 * Just a simple version for now. We can also ignore most function
2106 * calls[1] and self-references[2].
2108 * [1] Excluding volatile (TODAY, ...) and those that can create references
2109 * outside the arguments (INDIRECT).
2111 * [2] References that print like A1 when used in A1.
2113 static gboolean
2114 cond_expr_harmless (GnmExpr const *expr)
2116 GnmValue const *v = gnm_expr_get_constant (expr);
2117 if (v && !VALUE_IS_CELLRANGE (v))
2118 return TRUE;
2120 return FALSE;
2124 void
2125 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
2127 GnmStyleConditions *sc;
2128 Sheet *sheet;
2130 g_return_if_fail (style != NULL);
2131 g_return_if_fail (r != NULL);
2133 sheet = style->linked_sheet;
2136 * Conditional formatting.
2138 * We need to trigger a reformatting of the cell if a cell referenced
2139 * by the condition changes.
2141 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2142 ? gnm_style_get_conditions (style)
2143 : NULL;
2144 if (sc) {
2145 GPtrArray const *conds = gnm_style_conditions_details (sc);
2146 guint ui;
2147 if (debug_style_deps ())
2148 g_printerr ("Linking %s for %p\n",
2149 range_as_string (r), style);
2150 for (ui = 0; conds && ui < conds->len; ui++) {
2151 GnmStyleCond const *c = g_ptr_array_index (conds, ui);
2152 guint ei;
2154 for (ei = 0; ei < 2; ei++) {
2155 GnmExprTop const *texpr =
2156 gnm_style_cond_get_expr (c, ei);
2157 if (!texpr ||
2158 cond_expr_harmless (texpr->expr))
2159 continue;
2160 if (!style->deps)
2161 style->deps = g_ptr_array_new ();
2162 gnm_dep_style_dependency
2163 (sheet, texpr, r, style->deps);
2169 * Validations.
2171 * We can probably ignore those. If a dependent cell changes such
2172 * that a validation condition is no longer satisfied, it is
2173 * grandfathered in as valid.
2176 /* The style owns the deps. */
2179 void
2180 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2182 unsigned ui, k;
2184 g_return_if_fail (style != NULL);
2185 g_return_if_fail (r != NULL);
2187 if (!style->deps)
2188 return;
2190 for (ui = k = 0; ui < style->deps->len; ui++) {
2191 GnmDependent *dep = g_ptr_array_index (style->deps, ui);
2192 GnmCellPos const *pos = dependent_pos (dep);
2194 if (range_contains (r, pos->col, pos->row)) {
2195 if (debug_style_deps ())
2196 g_printerr ("Unlinking %s for %p\n",
2197 cellpos_as_string (pos), style);
2198 dependent_set_expr (dep, NULL);
2199 g_free (dep);
2200 } else {
2201 g_ptr_array_index (style->deps, k) = dep;
2202 k++;
2206 g_ptr_array_set_size (style->deps, k);
2211 * gnm_style_visible_in_blank:
2212 * @style: style to query
2214 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2215 * that means if it has a background or a visible border.
2217 gboolean
2218 gnm_style_visible_in_blank (GnmStyle const *style)
2220 GnmStyleElement i;
2222 g_return_val_if_fail (style != NULL, FALSE);
2224 if (elem_is_set (style, MSTYLE_PATTERN) &&
2225 gnm_style_get_pattern (style) > 0)
2226 return TRUE;
2228 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2229 if (elem_is_set (style, i) &&
2230 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2231 return TRUE;
2233 return FALSE;
2236 static void
2237 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2239 attr->start_index = 0;
2240 attr->end_index = G_MAXINT;
2241 pango_attr_list_insert (attrs, attr);
2245 * gnm_style_get_pango_attrs :
2246 * @style: #GnmStyle
2248 PangoAttrList *
2249 gnm_style_get_pango_attrs (GnmStyle const *style,
2250 PangoContext *context,
2251 double zoom)
2253 PangoAttrList *l;
2254 GnmUnderline ul;
2255 GnmFont *font = gnm_style_get_font (style, context);
2257 if (style->pango_attrs) {
2258 if (zoom == style->pango_attrs_zoom) {
2259 pango_attr_list_ref (style->pango_attrs);
2260 return style->pango_attrs;
2262 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2265 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2266 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2267 ((GnmStyle *)style)->pango_attrs_height = -1;
2269 /* Foreground colour. */
2270 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2271 if (0) {
2272 GnmColor const *fore = style->color.font;
2273 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2276 /* Handle underlining. */
2277 ul = gnm_style_get_font_uline (style);
2278 if (ul != UNDERLINE_NONE)
2279 add_attr (l,
2280 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2282 /* Handle strikethrough. */
2283 if (gnm_style_get_font_strike (style))
2284 add_attr (l, pango_attr_strikethrough_new (TRUE));
2286 /* Handle script. */
2287 switch (gnm_style_get_font_script (style)) {
2288 default :
2289 case GO_FONT_SCRIPT_STANDARD :
2290 break;
2291 case GO_FONT_SCRIPT_SUB :
2292 add_attr (l, go_pango_attr_subscript_new (TRUE));
2293 break;
2294 case GO_FONT_SCRIPT_SUPER :
2295 add_attr (l, go_pango_attr_superscript_new (TRUE));
2296 break;
2299 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2301 if (zoom != 1)
2302 add_attr (l, pango_attr_scale_new (zoom));
2304 pango_attr_list_ref (l);
2305 return l;
2308 PangoAttrList *
2309 gnm_style_generate_attrs_full (GnmStyle const *style)
2311 GnmColor const *fore = style->color.font;
2312 PangoAttrList *l = pango_attr_list_new ();
2314 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2315 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2316 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2317 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2318 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2319 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2320 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2321 add_attr (l, pango_attr_strikethrough_new
2322 (gnm_style_get_font_strike (style)));
2323 add_attr (l, pango_attr_underline_new
2324 (gnm_translate_underline_to_pango
2325 (gnm_style_get_font_uline (style))));
2326 return l;
2330 gnm_style_get_pango_height (GnmStyle const *style,
2331 PangoContext *context,
2332 double zoom)
2334 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2336 if (style->pango_attrs_height == -1) {
2337 int h;
2338 PangoLayout *layout = pango_layout_new (context);
2339 GOFormat const *fmt;
2340 gboolean requires_translation = FALSE;
2342 fmt = gnm_style_get_format (style);
2343 if (!go_format_is_general (fmt)) {
2344 GOFormatDetails details;
2345 go_format_get_details (fmt, &details, NULL);
2346 if (details.family == GO_FORMAT_SCIENTIFIC &&
2347 details.use_markup) {
2348 PangoAttribute *a
2349 = go_pango_attr_superscript_new (TRUE);
2350 /* We want to superscript the "-01" in the */
2351 /* string "+1.23456789E-01" */
2352 a->start_index = 12;
2353 a->end_index = 15;
2354 pango_attr_list_insert (attrs, a);
2355 requires_translation = TRUE;
2358 pango_layout_set_attributes (layout, attrs);
2359 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2360 if (requires_translation)
2361 go_pango_translate_layout (layout);
2362 pango_layout_get_pixel_size (layout, NULL, &h);
2363 g_object_unref (layout);
2364 ((GnmStyle *)style)->pango_attrs_height = h;
2367 pango_attr_list_unref (attrs);
2368 return style->pango_attrs_height;
2372 void
2373 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2375 switch (attr->klass->type) {
2376 case PANGO_ATTR_FAMILY :
2377 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2378 break;
2379 case PANGO_ATTR_SIZE :
2380 gnm_style_set_font_size (style,
2381 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2382 break;
2383 case PANGO_ATTR_STYLE :
2384 gnm_style_set_font_italic (style,
2385 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2386 break;
2387 case PANGO_ATTR_WEIGHT :
2388 gnm_style_set_font_bold (style,
2389 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2390 break;
2391 case PANGO_ATTR_FOREGROUND :
2392 gnm_style_set_font_color (style, gnm_color_new_pango (
2393 &((PangoAttrColor *)attr)->color));
2394 break;
2395 case PANGO_ATTR_UNDERLINE :
2396 gnm_style_set_font_uline
2397 (style, gnm_translate_underline_from_pango
2398 (((PangoAttrInt *)attr)->value));
2399 break;
2400 case PANGO_ATTR_STRIKETHROUGH :
2401 gnm_style_set_font_strike (style,
2402 ((PangoAttrInt *)attr)->value != 0);
2403 break;
2404 default : {
2405 gboolean script_seen = FALSE, script_set = FALSE;
2406 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2407 script_seen = TRUE;
2408 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2409 script_set = TRUE;
2410 gnm_style_set_font_script
2411 (style, GO_FONT_SCRIPT_SUPER);
2413 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2414 script_seen = TRUE;
2415 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2416 script_set = TRUE;
2417 gnm_style_set_font_script
2418 (style, GO_FONT_SCRIPT_SUB);
2421 if (script_seen && !script_set)
2422 gnm_style_set_font_script
2423 (style, GO_FONT_SCRIPT_STANDARD);
2424 break; /* ignored */
2429 /* ------------------------------------------------------------------------- */
2431 static void
2432 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2434 if (color)
2435 g_printerr ("\t%s: %x:%x:%x%s\n",
2436 gnm_style_element_name [elem],
2437 GO_COLOR_UINT_R (color->go_color),
2438 GO_COLOR_UINT_G (color->go_color),
2439 GO_COLOR_UINT_B (color->go_color),
2440 color->is_auto ? " auto" : "");
2441 else
2442 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2445 static void
2446 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2448 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2449 if (border)
2450 g_printerr ("%d\n", border->line_type);
2451 else
2452 g_printerr ("blank\n");
2456 * gnm_style_dump:
2457 * @style: style to dump
2459 * This function dumps the given style's contents to stderr. This is meant
2460 * for debug purposes only and doesn't do a very good job for, for example,
2461 * conditional style settings.
2463 void
2464 gnm_style_dump (GnmStyle const *style)
2466 int i;
2468 g_printerr ("Style Refs %d\n", style->ref_count);
2469 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2470 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2471 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2472 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2474 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2475 if (elem_is_set (style, i))
2476 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2478 if (elem_is_set (style, MSTYLE_PATTERN))
2479 g_printerr ("\tpattern %d\n", style->pattern);
2480 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2481 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2482 if (elem_is_set (style, MSTYLE_FONT_NAME))
2483 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2484 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2485 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2486 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2487 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2488 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2489 switch (style->font_detail.underline) {
2490 default :
2491 case UNDERLINE_NONE :
2492 g_printerr ("\tno underline\n"); break;
2493 case UNDERLINE_SINGLE :
2494 g_printerr ("\tsingle underline\n"); break;
2495 case UNDERLINE_DOUBLE :
2496 g_printerr ("\tdouble underline\n"); break;
2498 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2499 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2500 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2501 switch (style->font_detail.script) {
2502 case GO_FONT_SCRIPT_SUB :
2503 g_printerr ("\tsubscript\n"); break;
2504 default :
2505 case GO_FONT_SCRIPT_STANDARD :
2506 g_printerr ("\tno super or sub\n"); break;
2507 case GO_FONT_SCRIPT_SUPER :
2508 g_printerr ("\tsuperscript\n"); break;
2510 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2511 g_printerr ("\tsize %f\n", style->font_detail.size);
2512 if (elem_is_set (style, MSTYLE_FORMAT)) {
2513 const char *fmt = go_format_as_XL (style->format);
2514 g_printerr ("\tformat '%s'\n", fmt);
2516 if (elem_is_set (style, MSTYLE_ALIGN_V))
2517 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2518 if (elem_is_set (style, MSTYLE_ALIGN_H))
2519 g_printerr ("\thalign %hd\n", (short)style->h_align);
2520 if (elem_is_set (style, MSTYLE_INDENT))
2521 g_printerr ("\tindent %d\n", style->indent);
2522 if (elem_is_set (style, MSTYLE_ROTATION))
2523 g_printerr ("\trotation %d\n", style->rotation);
2524 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2525 g_printerr ("\ttext dir %d\n", style->text_dir);
2526 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2527 g_printerr ("\twrap text %d\n", style->wrap_text);
2528 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2529 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2530 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2531 g_printerr ("\tlocked %d\n", style->contents_locked);
2532 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2533 g_printerr ("\thidden %d\n", style->contents_hidden);
2534 if (elem_is_set (style, MSTYLE_VALIDATION))
2535 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2536 if (elem_is_set (style, MSTYLE_HLINK))
2537 g_printerr ("\thlink %p\n", (void *)style->hlink);
2538 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2539 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2540 if (elem_is_set (style, MSTYLE_CONDITIONS))
2541 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2544 /* ------------------------------------------------------------------------- */
2547 * gnm_style_init: (skip)
2549 void
2550 gnm_style_init (void)
2552 #if USE_MSTYLE_POOL
2553 gnm_style_pool =
2554 go_mem_chunk_new ("style pool",
2555 sizeof (GnmStyle),
2556 16 * 1024 - 128);
2557 #endif
2560 #if USE_MSTYLE_POOL
2561 static void
2562 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2564 GnmStyle *style = data;
2565 g_printerr ("Leaking style at %p.\n", (void *)style);
2566 gnm_style_dump (style);
2568 #endif
2571 * gnm_style_shutdown: (skip)
2573 void
2574 gnm_style_shutdown (void)
2576 #if USE_MSTYLE_POOL
2577 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2578 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2579 gnm_style_pool = NULL;
2580 #endif