Introspecion and doc fixes.
[gnumeric.git] / src / mstyle.c
bloba114c07a76eab648860395bb3ee05887ada44ab8
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 to query
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);
1364 * gnm_style_set_font_color:
1365 * @style: #GnmStyle to change
1366 * @col: (transfer full): #GnmColor
1368 * Set the color used for fonts.
1370 void
1371 gnm_style_set_font_color (GnmStyle *style, GnmColor *col)
1373 g_return_if_fail (style != NULL);
1374 g_return_if_fail (col != NULL);
1376 elem_changed (style, MSTYLE_FONT_COLOR);
1377 if (elem_is_set (style, MSTYLE_FONT_COLOR))
1378 style_color_unref (style->color.font);
1379 else
1380 elem_set (style, MSTYLE_FONT_COLOR);
1381 elem_changed (style, MSTYLE_FONT_COLOR);
1382 style->color.font = col;
1383 gnm_style_clear_pango (style);
1387 * gnm_style_set_back_color:
1388 * @style: #GnmStyle to change
1389 * @col: (transfer full): #GnmColor
1391 * Assigns @col as the background of @style.
1393 * NOTE: the background colour is only visibile if GnmStyle::pattern > 0
1395 void
1396 gnm_style_set_back_color (GnmStyle *style, GnmColor *col)
1398 g_return_if_fail (style != NULL);
1399 g_return_if_fail (col != NULL);
1401 elem_changed (style, MSTYLE_COLOR_BACK);
1402 if (elem_is_set (style, MSTYLE_COLOR_BACK))
1403 style_color_unref (style->color.back);
1404 else
1405 elem_set (style, MSTYLE_COLOR_BACK);
1406 style->color.back = col;
1407 gnm_style_clear_pango (style);
1411 * gnm_style_set_pattern_color:
1412 * @style: #GnmStyle to change
1413 * @col: (transfer full): #GnmColor
1415 * Set the color used for pattern.
1417 void
1418 gnm_style_set_pattern_color (GnmStyle *style, GnmColor *col)
1420 g_return_if_fail (style != NULL);
1421 g_return_if_fail (col != NULL);
1423 elem_changed (style, MSTYLE_COLOR_PATTERN);
1424 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1425 style_color_unref (style->color.pattern);
1426 else
1427 elem_set (style, MSTYLE_COLOR_PATTERN);
1428 style->color.pattern = col;
1429 gnm_style_clear_pango (style);
1433 * gnm_style_get_font_color:
1434 * @style: #GnmStyle to query
1436 * Returns: (transfer none) (nullable): #GnmColor used for font.
1438 GnmColor *
1439 gnm_style_get_font_color (GnmStyle const *style)
1441 g_return_val_if_fail (style != NULL, NULL);
1442 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_COLOR), NULL);
1443 return style->color.font;
1447 * gnm_style_get_back_color:
1448 * @style: #GnmStyle to query
1450 * Returns: (transfer none) (nullable): #GnmColor used for background.
1452 GnmColor *
1453 gnm_style_get_back_color (GnmStyle const *style)
1455 g_return_val_if_fail (style != NULL, NULL);
1456 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_BACK), NULL);
1457 return style->color.back;
1461 * gnm_style_get_pattern_color:
1462 * @style: #GnmStyle to query
1464 * Returns: (transfer none) (nullable): #GnmColor used for pattern.
1466 GnmColor *
1467 gnm_style_get_pattern_color (GnmStyle const *style)
1469 g_return_val_if_fail (style != NULL, NULL);
1470 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_PATTERN), NULL);
1471 return style->color.pattern;
1475 * gnm_style_set_border:
1476 * @style: #GnmStyle to change
1477 * @elem: Border element
1478 * @border: (transfer full) (nullable): new #GnmBorder for @style.
1480 void
1481 gnm_style_set_border (GnmStyle *style, GnmStyleElement elem,
1482 GnmBorder *border)
1484 g_return_if_fail (style != NULL);
1486 /* NOTE : It is legal for border to be NULL */
1487 switch (elem) {
1488 case MSTYLE_ANY_BORDER:
1489 elem_changed (style, elem);
1490 elem_set (style, elem);
1491 elem -= MSTYLE_BORDER_TOP;
1492 gnm_style_border_unref (style->borders[elem]);
1493 style->borders[elem] = border;
1494 break;
1495 default:
1496 g_warning ("Not a border element");
1497 break;
1502 * gnm_style_get_border:
1503 * @style: #GnmStyle to query
1504 * @elem: Border element
1506 * Returns: (transfer none) (nullable): The #GnmBorder for a single
1507 * border element.
1509 GnmBorder *
1510 gnm_style_get_border (GnmStyle const *style, GnmStyleElement elem)
1512 g_return_val_if_fail (style != NULL, NULL);
1514 switch (elem) {
1515 case MSTYLE_ANY_BORDER:
1516 return style->borders[elem - MSTYLE_BORDER_TOP ];
1518 default:
1519 g_warning ("Not a border element");
1520 return NULL;
1525 * gnm_style_set_pattern:
1526 * @style: #GnmStyle to change
1527 * @pattern: pattern code
1529 void
1530 gnm_style_set_pattern (GnmStyle *style, int pattern)
1532 g_return_if_fail (style != NULL);
1533 g_return_if_fail (pattern >= 0);
1534 g_return_if_fail (pattern < GNM_PATTERNS_MAX);
1536 elem_changed (style, MSTYLE_PATTERN);
1537 elem_set (style, MSTYLE_PATTERN);
1538 style->pattern = pattern;
1542 gnm_style_get_pattern (GnmStyle const *style)
1544 g_return_val_if_fail (style != NULL, 0);
1545 g_return_val_if_fail (elem_is_set (style, MSTYLE_PATTERN), 0);
1547 return style->pattern;
1551 * gnm_style_get_font:
1552 * @style: #GnmStyle to query
1553 * @context: #PangoContext
1555 * Returns: (transfer none): GnmFont implied by @style.
1557 GnmFont *
1558 gnm_style_get_font (GnmStyle const *style, PangoContext *context)
1560 g_return_val_if_fail (style != NULL, NULL);
1562 if (!style->font || style->font_context != context) {
1563 char const *name;
1564 gboolean bold, italic;
1565 double size;
1567 gnm_style_clear_font ((GnmStyle *)style);
1569 if (elem_is_set (style, MSTYLE_FONT_NAME))
1570 name = gnm_style_get_font_name (style);
1571 else
1572 name = DEFAULT_FONT;
1574 if (elem_is_set (style, MSTYLE_FONT_BOLD))
1575 bold = gnm_style_get_font_bold (style);
1576 else
1577 bold = FALSE;
1579 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
1580 italic = gnm_style_get_font_italic (style);
1581 else
1582 italic = FALSE;
1584 if (elem_is_set (style, MSTYLE_FONT_SIZE))
1585 size = gnm_style_get_font_size (style);
1586 else
1587 size = DEFAULT_SIZE;
1589 ((GnmStyle *)style)->font =
1590 gnm_font_new (context, name, size, bold, italic);
1591 ((GnmStyle *)style)->font_context = g_object_ref (context);
1594 return style->font;
1598 * gnm_style_set_font_name:
1599 * @style: #GnmStyle to change
1600 * @name: the font name as a string
1602 void
1603 gnm_style_set_font_name (GnmStyle *style, char const *name)
1605 g_return_if_fail (name != NULL);
1606 g_return_if_fail (style != NULL);
1608 elem_changed (style, MSTYLE_FONT_NAME);
1609 if (elem_is_set (style, MSTYLE_FONT_NAME))
1610 go_string_unref (style->font_detail.name);
1611 else
1612 elem_set (style, MSTYLE_FONT_NAME);
1613 style->font_detail.name = go_string_new (name);
1614 gnm_style_clear_font (style);
1615 gnm_style_clear_pango (style);
1619 * gnm_style_get_font_name:
1620 * @style: the style to query
1622 * Returns: (transfer none): the currently set font name
1624 char const *
1625 gnm_style_get_font_name (GnmStyle const *style)
1627 g_return_val_if_fail (style != NULL, NULL);
1628 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_NAME), NULL);
1630 return style->font_detail.name->str;
1634 * gnm_style_set_font_bold:
1635 * @style: #GnmStyle to change
1636 * @bold: %TRUE for bold, %FALSE for regular
1638 void
1639 gnm_style_set_font_bold (GnmStyle *style, gboolean bold)
1641 g_return_if_fail (style != NULL);
1643 elem_changed (style, MSTYLE_FONT_BOLD);
1644 elem_set (style, MSTYLE_FONT_BOLD);
1645 style->font_detail.bold = !!bold;
1646 gnm_style_clear_font (style);
1647 gnm_style_clear_pango (style);
1651 * gnm_style_get_font_bold:
1652 * @style: #GnmStyle to query
1654 * Returns: %TRUE if the style has a bold font.
1656 gboolean
1657 gnm_style_get_font_bold (GnmStyle const *style)
1659 g_return_val_if_fail (style != NULL, FALSE);
1660 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_BOLD), FALSE);
1662 return style->font_detail.bold;
1666 * gnm_style_set_font_italic:
1667 * @style: #GnmStyle to change
1668 * @italic: %TRUE for italic, %FALSE for regular
1670 void
1671 gnm_style_set_font_italic (GnmStyle *style, gboolean italic)
1673 g_return_if_fail (style != NULL);
1675 elem_changed (style, MSTYLE_FONT_ITALIC);
1676 elem_set (style, MSTYLE_FONT_ITALIC);
1677 style->font_detail.italic = !!italic;
1678 gnm_style_clear_font (style);
1679 gnm_style_clear_pango (style);
1683 * gnm_style_get_font_italic:
1684 * @style: #GnmStyle to query
1686 * Returns: %TRUE if the style has an italic font.
1688 gboolean
1689 gnm_style_get_font_italic (GnmStyle const *style)
1691 g_return_val_if_fail (style != NULL, FALSE);
1692 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_ITALIC), FALSE);
1694 return style->font_detail.italic;
1698 * gnm_style_set_font_uline:
1699 * @style: #GnmStyle to change
1700 * @ul: #GnmUnderline specifying type of underlining
1702 void
1703 gnm_style_set_font_uline (GnmStyle *style, GnmUnderline const underline)
1705 g_return_if_fail (style != NULL);
1706 g_return_if_fail (underline >= UNDERLINE_NONE && underline <= UNDERLINE_DOUBLE_LOW);
1708 elem_changed (style, MSTYLE_FONT_UNDERLINE);
1709 elem_set (style, MSTYLE_FONT_UNDERLINE);
1710 style->font_detail.underline = underline;
1711 gnm_style_clear_pango (style);
1715 * gnm_style_get_font_uline:
1716 * @style: #GnmStyle to query
1718 * Returns: #GnmUnderline specifying type of underlining
1720 GnmUnderline
1721 gnm_style_get_font_uline (GnmStyle const *style)
1723 g_return_val_if_fail (style != NULL, UNDERLINE_NONE);
1724 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_UNDERLINE), UNDERLINE_NONE);
1726 return style->font_detail.underline;
1730 * gnm_style_set_font_strike:
1731 * @style: #GnmStyle to change
1732 * @strike: %TRUE for strikethrough, %FALSE for regular
1734 void
1735 gnm_style_set_font_strike (GnmStyle *style, gboolean strikethrough)
1737 g_return_if_fail (style != NULL);
1739 elem_changed (style, MSTYLE_FONT_STRIKETHROUGH);
1740 elem_set (style, MSTYLE_FONT_STRIKETHROUGH);
1741 style->font_detail.strikethrough = !!strikethrough;
1742 gnm_style_clear_pango (style);
1746 * gnm_style_get_font_strike:
1747 * @style: #GnmStyle to query
1749 * Returns: %TRUE for strikethrough, %FALSE for regular
1751 gboolean
1752 gnm_style_get_font_strike (GnmStyle const *style)
1754 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
1756 return style->font_detail.strikethrough;
1760 * gnm_style_set_font_script:
1761 * @style: #GnmStyle to change
1762 * @script: #GOFontScript specifying super or subscript
1764 void
1765 gnm_style_set_font_script (GnmStyle *style, GOFontScript script)
1767 g_return_if_fail (style != NULL);
1768 elem_changed (style, MSTYLE_FONT_SCRIPT);
1769 elem_set (style, MSTYLE_FONT_SCRIPT);
1770 style->font_detail.script = script;
1771 gnm_style_clear_pango (style);
1775 * gnm_style_get_font_script:
1776 * @style: #GnmStyle to query
1778 * Returns: #GOFontScript specifying super or subscript
1780 GOFontScript
1781 gnm_style_get_font_script (GnmStyle const *style)
1783 g_return_val_if_fail (style != NULL, GO_FONT_SCRIPT_STANDARD);
1784 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SCRIPT), GO_FONT_SCRIPT_STANDARD);
1786 return style->font_detail.script;
1790 * gnm_style_set_font_size:
1791 * @style: #GnmStyle to change
1792 * @size: Font size in points
1794 void
1795 gnm_style_set_font_size (GnmStyle *style, double size)
1797 g_return_if_fail (style != NULL);
1798 g_return_if_fail (size >= 1.);
1799 elem_changed (style, MSTYLE_FONT_SIZE);
1800 elem_set (style, MSTYLE_FONT_SIZE);
1801 style->font_detail.size = size;
1802 gnm_style_clear_font (style);
1803 gnm_style_clear_pango (style);
1807 * gnm_style_get_font_size:
1808 * @style: #GnmStyle to query
1810 * Returns: Font size in points
1812 double
1813 gnm_style_get_font_size (GnmStyle const *style)
1815 g_return_val_if_fail (style != NULL, 12.0);
1816 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SIZE), 12.0);
1818 return style->font_detail.size;
1822 * gnm_style_set_format:
1823 * @style: #GnmStyle to change
1824 * @fmt: #GOFormat
1826 void
1827 gnm_style_set_format (GnmStyle *style, GOFormat const *fmt)
1829 g_return_if_fail (style != NULL);
1830 g_return_if_fail (fmt != NULL);
1832 elem_changed (style, MSTYLE_FORMAT);
1833 go_format_ref (fmt);
1834 elem_clear_contents (style, MSTYLE_FORMAT);
1835 elem_set (style, MSTYLE_FORMAT);
1836 style->format = fmt;
1840 * gnm_style_set_format_text:
1841 * @style: mstyle to change.
1842 * @format: An *untranslated* format string.
1844 void
1845 gnm_style_set_format_text (GnmStyle *style, char const *format)
1847 GOFormat *sf;
1849 g_return_if_fail (style != NULL);
1850 g_return_if_fail (format != NULL);
1852 sf = go_format_new_from_XL (format);
1853 gnm_style_set_format (style, sf);
1854 go_format_unref (sf);
1858 * gnm_style_get_format:
1859 * @style: #GnmStyle to query
1861 * Returns: (transfer none): #GOFormat
1863 const GOFormat *
1864 gnm_style_get_format (GnmStyle const *style)
1866 g_return_val_if_fail (style != NULL, NULL);
1867 g_return_val_if_fail (elem_is_set (style, MSTYLE_FORMAT), NULL);
1869 return style->format;
1873 * gnm_style_set_align_h:
1874 * @style: #GnmStyle to change
1875 * @a: A #GnmHAlign
1877 void
1878 gnm_style_set_align_h (GnmStyle *style, GnmHAlign a)
1880 g_return_if_fail (style != NULL);
1882 elem_changed (style, MSTYLE_ALIGN_H);
1883 elem_set (style, MSTYLE_ALIGN_H);
1884 style->h_align = a;
1888 * gnm_style_get_align_h:
1889 * @style: #GnmStyle to query
1891 * Returns: A #GnmHAlign
1893 GnmHAlign
1894 gnm_style_get_align_h (GnmStyle const *style)
1896 g_return_val_if_fail (style != NULL, GNM_HALIGN_LEFT);
1897 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), GNM_HALIGN_LEFT);
1899 return style->h_align;
1903 * gnm_style_set_align_v:
1904 * @style: #GnmStyle to change
1905 * @a: A #GnmVAlign
1907 void
1908 gnm_style_set_align_v (GnmStyle *style, GnmVAlign a)
1910 g_return_if_fail (style != NULL);
1912 elem_changed (style, MSTYLE_ALIGN_V);
1913 elem_set (style, MSTYLE_ALIGN_V);
1914 style->v_align = a;
1918 * gnm_style_get_align_v:
1919 * @style: #GnmStyle to query
1921 * Returns: A #GnmVAlign
1923 GnmVAlign
1924 gnm_style_get_align_v (GnmStyle const *style)
1926 g_return_val_if_fail (style != NULL, GNM_VALIGN_TOP);
1927 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), GNM_VALIGN_TOP);
1929 return style->v_align;
1933 * gnm_style_set_indent:
1934 * @style: #GnmStyle to change
1935 * @i: Indentation amount
1937 void
1938 gnm_style_set_indent (GnmStyle *style, int i)
1940 g_return_if_fail (style != NULL);
1942 elem_changed (style, MSTYLE_INDENT);
1943 elem_set (style, MSTYLE_INDENT);
1944 style->indent = i;
1948 * gnm_style_get_indent:
1949 * @style: #GnmStyle to query
1951 * Returns: Indentation amount
1954 gnm_style_get_indent (GnmStyle const *style)
1956 g_return_val_if_fail (style != NULL, 0);
1957 g_return_val_if_fail (elem_is_set (style, MSTYLE_INDENT), 0);
1959 return style->indent;
1963 * gnm_style_set_rotation:
1964 * @style: #GnmStyle to change
1965 * @r: Rotation in degrees relative to horizontal
1967 void
1968 gnm_style_set_rotation (GnmStyle *style, int rot_deg)
1970 g_return_if_fail (style != NULL);
1972 elem_changed (style, MSTYLE_ROTATION);
1973 elem_set (style, MSTYLE_ROTATION);
1974 style->rotation = rot_deg;
1978 * gnm_style_get_rotation:
1979 * @style: #GnmStyle to query
1981 * Returns: Rotation in degrees relative to horizontal
1984 gnm_style_get_rotation (GnmStyle const *style)
1986 g_return_val_if_fail (style != NULL, 0);
1987 g_return_val_if_fail (elem_is_set (style, MSTYLE_ROTATION), 0);
1989 return style->rotation;
1993 * gnm_style_set_text_dir:
1994 * @style: #GnmStyle to change
1995 * @text_dir: A #GnmTextDir
1997 void
1998 gnm_style_set_text_dir (GnmStyle *style, GnmTextDir text_dir)
2000 g_return_if_fail (style != NULL);
2002 elem_changed (style, MSTYLE_TEXT_DIR);
2003 elem_set (style, MSTYLE_TEXT_DIR);
2004 style->text_dir = text_dir;
2008 * gnm_style_get_text_dir:
2009 * @style: #GnmStyle to query
2011 * Returns: A #GnmTextDir
2013 GnmTextDir
2014 gnm_style_get_text_dir (GnmStyle const *style)
2016 g_return_val_if_fail (style != NULL, GNM_TEXT_DIR_CONTEXT);
2017 g_return_val_if_fail (elem_is_set (style, MSTYLE_TEXT_DIR), GNM_TEXT_DIR_CONTEXT);
2019 return style->text_dir;
2023 * gnm_style_set_wrap_text:
2024 * @style: #GnmStyle to change
2025 * @f: %TRUE for wrapping, %FALSE for not
2027 void
2028 gnm_style_set_wrap_text (GnmStyle *style, gboolean f)
2030 g_return_if_fail (style != NULL);
2032 elem_changed (style, MSTYLE_WRAP_TEXT);
2033 elem_set (style, MSTYLE_WRAP_TEXT);
2034 style->wrap_text = !!f;
2038 * gnm_style_get_wrap_text:
2039 * @style: #GnmStyle to query
2041 * Returns: %TRUE for wrapping, %FALSE for not. See also
2042 * gnm_style_get_effective_wrap_text.
2044 gboolean
2045 gnm_style_get_wrap_text (GnmStyle const *style)
2047 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
2049 return style->wrap_text;
2053 * gnm_style_get_effective_wrap_text:
2054 * @style: #GnmStyle to query
2056 * Returns: %TRUE for wrapping, %FALSE for not. This will be %TRUE also
2057 * when either alignment is JUSTIFY.
2059 gboolean
2060 gnm_style_get_effective_wrap_text (GnmStyle const *style)
2062 g_return_val_if_fail (style != NULL, FALSE);
2063 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
2064 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), FALSE);
2065 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), FALSE);
2067 /* Note: GNM_HALIGN_GENERAL never expands to GNM_HALIGN_JUSTIFY. */
2068 return (style->wrap_text ||
2069 style->v_align == GNM_VALIGN_JUSTIFY ||
2070 style->v_align == GNM_VALIGN_DISTRIBUTED ||
2071 style->h_align == GNM_HALIGN_JUSTIFY);
2075 * gnm_style_set_shrink_to_fit:
2076 * @style: #GnmStyle to change
2077 * @f: %TRUE for shrink-to-fit, %FALSE for not
2079 void
2080 gnm_style_set_shrink_to_fit (GnmStyle *style, gboolean f)
2082 g_return_if_fail (style != NULL);
2084 elem_changed (style, MSTYLE_SHRINK_TO_FIT);
2085 elem_set (style, MSTYLE_SHRINK_TO_FIT);
2086 style->shrink_to_fit = !!f;
2090 * gnm_style_get_shrink_to_fit:
2091 * @style: #GnmStyle to query
2093 * Returns: %TRUE for shrink-to-fit, %FALSE for not
2095 gboolean
2096 gnm_style_get_shrink_to_fit (GnmStyle const *style)
2098 g_return_val_if_fail (style != NULL, FALSE);
2099 g_return_val_if_fail (elem_is_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
2101 return style->shrink_to_fit;
2105 * gnm_style_set_contents_locked:
2106 * @style: #GnmStyle to change
2107 * @f: %TRUE for locked, %FALSE for not
2109 void
2110 gnm_style_set_contents_locked (GnmStyle *style, gboolean f)
2112 g_return_if_fail (style != NULL);
2114 elem_changed (style, MSTYLE_CONTENTS_LOCKED);
2115 elem_set (style, MSTYLE_CONTENTS_LOCKED);
2116 style->contents_locked = !!f;
2120 * gnm_style_get_contents_locked:
2121 * @style: #GnmStyle to query
2123 * Returns: %TRUE for locked, %FALSE for not
2125 gboolean
2126 gnm_style_get_contents_locked (GnmStyle const *style)
2128 g_return_val_if_fail (style != NULL, FALSE);
2129 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_LOCKED), FALSE);
2131 return style->contents_locked;
2135 * gnm_style_set_contents_hidden:
2136 * @style: #GnmStyle to change
2137 * @f: %TRUE for hidden, %FALSE for not
2139 void
2140 gnm_style_set_contents_hidden (GnmStyle *style, gboolean f)
2142 g_return_if_fail (style != NULL);
2144 elem_changed (style, MSTYLE_CONTENTS_HIDDEN);
2145 elem_set (style, MSTYLE_CONTENTS_HIDDEN);
2146 style->contents_hidden = !!f;
2150 * gnm_style_get_contents_hidden:
2151 * @style: #GnmStyle to query
2153 * Return: %TRUE for hidden, %FALSE for not
2155 gboolean
2156 gnm_style_get_contents_hidden (GnmStyle const *style)
2158 g_return_val_if_fail (style != NULL, FALSE);
2159 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN), FALSE);
2161 return style->contents_hidden;
2165 * gnm_style_set_validation:
2166 * @style: #GnmStyle to change
2167 * @v: (transfer full): #GnmValidation
2169 void
2170 gnm_style_set_validation (GnmStyle *style, GnmValidation *v)
2172 g_return_if_fail (style != NULL);
2174 elem_clear_contents (style, MSTYLE_VALIDATION);
2175 elem_changed (style, MSTYLE_VALIDATION);
2176 elem_set (style, MSTYLE_VALIDATION);
2177 style->validation = v;
2181 * gnm_style_get_validation:
2182 * @style: #GnmStyle to query
2184 * Returns: (transfer none):
2186 GnmValidation const *
2187 gnm_style_get_validation (GnmStyle const *style)
2189 g_return_val_if_fail (style != NULL, NULL);
2190 g_return_val_if_fail (elem_is_set (style, MSTYLE_VALIDATION), NULL);
2192 return style->validation;
2196 * gnm_style_set_hlink:
2197 * @style: #GnmStyle to change
2198 * @lnk: (transfer full) (nullable): #GnmHLink
2200 * This sets a link for @style.
2202 void
2203 gnm_style_set_hlink (GnmStyle *style, GnmHLink *lnk)
2205 g_return_if_fail (style != NULL);
2207 elem_clear_contents (style, MSTYLE_HLINK);
2208 elem_changed (style, MSTYLE_HLINK);
2209 elem_set (style, MSTYLE_HLINK);
2210 style->hlink = lnk;
2214 * gnm_style_get_hlink:
2215 * @style: #GnmStyle to query
2217 * Returns: (transfer none) (nullable): the associated #GnmHLink.
2219 GnmHLink *
2220 gnm_style_get_hlink (GnmStyle const *style)
2222 g_return_val_if_fail (style != NULL, NULL);
2223 g_return_val_if_fail (elem_is_set (style, MSTYLE_HLINK), NULL);
2225 return style->hlink;
2229 * gnm_style_set_input_msg:
2230 * @style: #GnmStyle to change
2231 * @msg: (transfer full) (nullable): #GnmInputMsg
2233 * This sets an input message for @style.
2235 void
2236 gnm_style_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
2238 g_return_if_fail (style != NULL);
2240 elem_clear_contents (style, MSTYLE_INPUT_MSG);
2241 elem_changed (style, MSTYLE_INPUT_MSG);
2242 elem_set (style, MSTYLE_INPUT_MSG);
2243 style->input_msg = msg;
2247 * gnm_style_get_input_msg:
2248 * @style: #GnmStyle to query
2250 * Returns: (transfer none) (nullable): the currently set input message.
2252 GnmInputMsg *
2253 gnm_style_get_input_msg (GnmStyle const *style)
2255 g_return_val_if_fail (style != NULL, NULL);
2256 g_return_val_if_fail (elem_is_set (style, MSTYLE_INPUT_MSG), NULL);
2258 return style->input_msg;
2262 * gnm_style_set_conditions:
2263 * @style: #GnmStyle to change
2264 * @sc: (transfer full): #GnmStyleConditions
2266 * This sets conditional style for @style.
2268 void
2269 gnm_style_set_conditions (GnmStyle *style, GnmStyleConditions *sc)
2271 g_return_if_fail (style != NULL);
2273 elem_clear_contents (style, MSTYLE_CONDITIONS);
2274 elem_changed (style, MSTYLE_CONDITIONS);
2275 elem_set (style, MSTYLE_CONDITIONS);
2276 style->conditions = sc;
2280 * gnm_style_get_conditions:
2281 * @style: #GnmStyle to query
2283 * Returns: (transfer none) (nullable): the currently set conditional style.
2285 GnmStyleConditions *
2286 gnm_style_get_conditions (GnmStyle const *style)
2288 g_return_val_if_fail (style != NULL, NULL);
2289 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2290 return style->conditions;
2294 * gnm_style_get_cond_style:
2295 * @style: #GnmStyle to query
2296 * @ix: The index of the condition for which style is desired
2298 * Returns: (transfer none): the resulting style from applying the condition's
2299 * style overlay onto @style.
2301 GnmStyle const *
2302 gnm_style_get_cond_style (GnmStyle const *style, int ix)
2304 g_return_val_if_fail (style != NULL, NULL);
2305 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2306 g_return_val_if_fail (style->cond_styles != NULL, NULL);
2308 g_return_val_if_fail (ix >= 0 && (unsigned)ix < style->cond_styles->len, NULL);
2310 return g_ptr_array_index (style->cond_styles, ix);
2315 static gboolean
2316 debug_style_deps (void)
2318 static int debug = -1;
2319 if (debug < 0)
2320 debug = gnm_debug_flag ("style-deps");
2321 return debug;
2325 * Just a simple version for now. We can also ignore most function
2326 * calls[1] and self-references[2].
2328 * [1] Excluding volatile (TODAY, ...) and those that can create references
2329 * outside the arguments (INDIRECT).
2331 * [2] References that print like A1 when used in A1.
2333 static gboolean
2334 cond_expr_harmless (GnmExpr const *expr)
2336 GnmValue const *v = gnm_expr_get_constant (expr);
2337 if (v && !VALUE_IS_CELLRANGE (v))
2338 return TRUE;
2340 return FALSE;
2344 void
2345 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
2347 GnmStyleConditions *sc;
2348 Sheet *sheet;
2350 g_return_if_fail (style != NULL);
2351 g_return_if_fail (r != NULL);
2353 sheet = style->linked_sheet;
2356 * Conditional formatting.
2358 * We need to trigger a reformatting of the cell if a cell referenced
2359 * by the condition changes.
2361 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2362 ? gnm_style_get_conditions (style)
2363 : NULL;
2364 if (sc) {
2365 GPtrArray const *conds = gnm_style_conditions_details (sc);
2366 guint ui;
2367 if (debug_style_deps ())
2368 g_printerr ("Linking %s for %p\n",
2369 range_as_string (r), style);
2370 for (ui = 0; conds && ui < conds->len; ui++) {
2371 GnmStyleCond const *c = g_ptr_array_index (conds, ui);
2372 guint ei;
2374 for (ei = 0; ei < 2; ei++) {
2375 GnmExprTop const *texpr =
2376 gnm_style_cond_get_expr (c, ei);
2377 if (!texpr ||
2378 cond_expr_harmless (texpr->expr))
2379 continue;
2380 if (!style->deps)
2381 style->deps = g_ptr_array_new ();
2382 gnm_dep_style_dependency
2383 (sheet, texpr, r, style->deps);
2389 * Validations.
2391 * We can probably ignore those. If a dependent cell changes such
2392 * that a validation condition is no longer satisfied, it is
2393 * grandfathered in as valid.
2396 /* The style owns the deps. */
2399 void
2400 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2402 unsigned ui, k;
2404 g_return_if_fail (style != NULL);
2405 g_return_if_fail (r != NULL);
2407 if (!style->deps)
2408 return;
2410 for (ui = k = 0; ui < style->deps->len; ui++) {
2411 GnmDependent *dep = g_ptr_array_index (style->deps, ui);
2412 GnmCellPos const *pos = dependent_pos (dep);
2414 if (range_contains (r, pos->col, pos->row)) {
2415 if (debug_style_deps ())
2416 g_printerr ("Unlinking %s for %p\n",
2417 cellpos_as_string (pos), style);
2418 dependent_set_expr (dep, NULL);
2419 g_free (dep);
2420 } else {
2421 g_ptr_array_index (style->deps, k) = dep;
2422 k++;
2426 g_ptr_array_set_size (style->deps, k);
2431 * gnm_style_visible_in_blank:
2432 * @style: style to query
2434 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2435 * that means if it has a background or a visible border.
2437 gboolean
2438 gnm_style_visible_in_blank (GnmStyle const *style)
2440 GnmStyleElement i;
2442 g_return_val_if_fail (style != NULL, FALSE);
2444 if (elem_is_set (style, MSTYLE_PATTERN) &&
2445 gnm_style_get_pattern (style) > 0)
2446 return TRUE;
2448 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2449 if (elem_is_set (style, i) &&
2450 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2451 return TRUE;
2453 return FALSE;
2456 static void
2457 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2459 attr->start_index = 0;
2460 attr->end_index = G_MAXINT;
2461 pango_attr_list_insert (attrs, attr);
2465 * gnm_style_generate_attrs:
2466 * @style: style to query
2468 * Returns: (transfer full): a #PangoAttrList with attributes matching
2469 * @style. Attributes where the default will serve are not included.
2470 * The foreground color is not included.
2472 PangoAttrList *
2473 gnm_style_get_pango_attrs (GnmStyle const *style,
2474 PangoContext *context,
2475 double zoom)
2477 PangoAttrList *l;
2478 GnmUnderline ul;
2479 GnmFont *font = gnm_style_get_font (style, context);
2481 if (style->pango_attrs) {
2482 if (zoom == style->pango_attrs_zoom) {
2483 pango_attr_list_ref (style->pango_attrs);
2484 return style->pango_attrs;
2486 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2489 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2490 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2491 ((GnmStyle *)style)->pango_attrs_height = -1;
2493 /* Foreground colour. */
2494 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2495 if (0) {
2496 GnmColor const *fore = style->color.font;
2497 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2500 /* Handle underlining. */
2501 ul = gnm_style_get_font_uline (style);
2502 if (ul != UNDERLINE_NONE)
2503 add_attr (l,
2504 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2506 /* Handle strikethrough. */
2507 if (gnm_style_get_font_strike (style))
2508 add_attr (l, pango_attr_strikethrough_new (TRUE));
2510 /* Handle script. */
2511 switch (gnm_style_get_font_script (style)) {
2512 default:
2513 case GO_FONT_SCRIPT_STANDARD:
2514 break;
2515 case GO_FONT_SCRIPT_SUB:
2516 add_attr (l, go_pango_attr_subscript_new (TRUE));
2517 break;
2518 case GO_FONT_SCRIPT_SUPER:
2519 add_attr (l, go_pango_attr_superscript_new (TRUE));
2520 break;
2523 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2525 if (zoom != 1)
2526 add_attr (l, pango_attr_scale_new (zoom));
2528 pango_attr_list_ref (l);
2529 return l;
2533 * gnm_style_generate_attrs_full:
2534 * @style: style to query
2536 * Returns: (transfer full): a #PangoAttrList with attributes matching
2537 * @style, even attributes where the default would have served.
2539 PangoAttrList *
2540 gnm_style_generate_attrs_full (GnmStyle const *style)
2542 GnmColor const *fore = style->color.font;
2543 PangoAttrList *l = pango_attr_list_new ();
2545 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2546 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2547 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2548 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2549 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2550 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2551 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2552 add_attr (l, pango_attr_strikethrough_new
2553 (gnm_style_get_font_strike (style)));
2554 add_attr (l, pango_attr_underline_new
2555 (gnm_translate_underline_to_pango
2556 (gnm_style_get_font_uline (style))));
2557 return l;
2561 gnm_style_get_pango_height (GnmStyle const *style,
2562 PangoContext *context,
2563 double zoom)
2565 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2567 if (style->pango_attrs_height == -1) {
2568 int h;
2569 PangoLayout *layout = pango_layout_new (context);
2570 GOFormat const *fmt;
2571 gboolean requires_translation = FALSE;
2573 fmt = gnm_style_get_format (style);
2574 if (!go_format_is_general (fmt)) {
2575 GOFormatDetails details;
2576 go_format_get_details (fmt, &details, NULL);
2577 if (details.family == GO_FORMAT_SCIENTIFIC &&
2578 details.use_markup) {
2579 PangoAttribute *a
2580 = go_pango_attr_superscript_new (TRUE);
2581 /* We want to superscript the "-01" in the */
2582 /* string "+1.23456789E-01" */
2583 a->start_index = 12;
2584 a->end_index = 15;
2585 pango_attr_list_insert (attrs, a);
2586 requires_translation = TRUE;
2589 pango_layout_set_attributes (layout, attrs);
2590 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2591 if (requires_translation)
2592 go_pango_translate_layout (layout);
2593 pango_layout_get_pixel_size (layout, NULL, &h);
2594 g_object_unref (layout);
2595 ((GnmStyle *)style)->pango_attrs_height = h;
2598 pango_attr_list_unref (attrs);
2599 return style->pango_attrs_height;
2603 void
2604 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2606 switch (attr->klass->type) {
2607 case PANGO_ATTR_FAMILY:
2608 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2609 break;
2610 case PANGO_ATTR_SIZE:
2611 gnm_style_set_font_size (style,
2612 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2613 break;
2614 case PANGO_ATTR_STYLE:
2615 gnm_style_set_font_italic (style,
2616 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2617 break;
2618 case PANGO_ATTR_WEIGHT:
2619 gnm_style_set_font_bold (style,
2620 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2621 break;
2622 case PANGO_ATTR_FOREGROUND:
2623 gnm_style_set_font_color (style, gnm_color_new_pango (
2624 &((PangoAttrColor *)attr)->color));
2625 break;
2626 case PANGO_ATTR_UNDERLINE:
2627 gnm_style_set_font_uline
2628 (style, gnm_translate_underline_from_pango
2629 (((PangoAttrInt *)attr)->value));
2630 break;
2631 case PANGO_ATTR_STRIKETHROUGH:
2632 gnm_style_set_font_strike (style,
2633 ((PangoAttrInt *)attr)->value != 0);
2634 break;
2635 default : {
2636 gboolean script_seen = FALSE, script_set = FALSE;
2637 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2638 script_seen = TRUE;
2639 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2640 script_set = TRUE;
2641 gnm_style_set_font_script
2642 (style, GO_FONT_SCRIPT_SUPER);
2644 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2645 script_seen = TRUE;
2646 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2647 script_set = TRUE;
2648 gnm_style_set_font_script
2649 (style, GO_FONT_SCRIPT_SUB);
2652 if (script_seen && !script_set)
2653 gnm_style_set_font_script
2654 (style, GO_FONT_SCRIPT_STANDARD);
2655 break; /* ignored */
2660 /* ------------------------------------------------------------------------- */
2662 static void
2663 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2665 if (color)
2666 g_printerr ("\t%s: %x:%x:%x%s\n",
2667 gnm_style_element_name [elem],
2668 GO_COLOR_UINT_R (color->go_color),
2669 GO_COLOR_UINT_G (color->go_color),
2670 GO_COLOR_UINT_B (color->go_color),
2671 color->is_auto ? " auto" : "");
2672 else
2673 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2676 static void
2677 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2679 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2680 if (border)
2681 g_printerr ("%d\n", border->line_type);
2682 else
2683 g_printerr ("blank\n");
2687 * gnm_style_dump:
2688 * @style: style to dump
2690 * This function dumps the given style's contents to stderr. This is meant
2691 * for debug purposes only and doesn't do a very good job for, for example,
2692 * conditional style settings.
2694 void
2695 gnm_style_dump (GnmStyle const *style)
2697 int i;
2699 g_printerr ("Style Refs %d\n", style->ref_count);
2700 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2701 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2702 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2703 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2705 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2706 if (elem_is_set (style, i))
2707 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2709 if (elem_is_set (style, MSTYLE_PATTERN))
2710 g_printerr ("\tpattern %d\n", style->pattern);
2711 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2712 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2713 if (elem_is_set (style, MSTYLE_FONT_NAME))
2714 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2715 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2716 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2717 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2718 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2719 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2720 switch (style->font_detail.underline) {
2721 default:
2722 case UNDERLINE_NONE:
2723 g_printerr ("\tno underline\n"); break;
2724 case UNDERLINE_SINGLE:
2725 g_printerr ("\tsingle underline\n"); break;
2726 case UNDERLINE_DOUBLE:
2727 g_printerr ("\tdouble underline\n"); break;
2729 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2730 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2731 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2732 switch (style->font_detail.script) {
2733 case GO_FONT_SCRIPT_SUB:
2734 g_printerr ("\tsubscript\n"); break;
2735 default:
2736 case GO_FONT_SCRIPT_STANDARD:
2737 g_printerr ("\tno super or sub\n"); break;
2738 case GO_FONT_SCRIPT_SUPER:
2739 g_printerr ("\tsuperscript\n"); break;
2741 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2742 g_printerr ("\tsize %f\n", style->font_detail.size);
2743 if (elem_is_set (style, MSTYLE_FORMAT)) {
2744 const char *fmt = go_format_as_XL (style->format);
2745 g_printerr ("\tformat '%s'\n", fmt);
2747 if (elem_is_set (style, MSTYLE_ALIGN_V))
2748 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2749 if (elem_is_set (style, MSTYLE_ALIGN_H))
2750 g_printerr ("\thalign %hd\n", (short)style->h_align);
2751 if (elem_is_set (style, MSTYLE_INDENT))
2752 g_printerr ("\tindent %d\n", style->indent);
2753 if (elem_is_set (style, MSTYLE_ROTATION))
2754 g_printerr ("\trotation %d\n", style->rotation);
2755 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2756 g_printerr ("\ttext dir %d\n", style->text_dir);
2757 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2758 g_printerr ("\twrap text %d\n", style->wrap_text);
2759 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2760 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2761 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2762 g_printerr ("\tlocked %d\n", style->contents_locked);
2763 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2764 g_printerr ("\thidden %d\n", style->contents_hidden);
2765 if (elem_is_set (style, MSTYLE_VALIDATION))
2766 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2767 if (elem_is_set (style, MSTYLE_HLINK))
2768 g_printerr ("\thlink %p\n", (void *)style->hlink);
2769 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2770 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2771 if (elem_is_set (style, MSTYLE_CONDITIONS))
2772 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2775 /* ------------------------------------------------------------------------- */
2778 * gnm_style_init: (skip)
2780 void
2781 gnm_style_init (void)
2783 #if USE_MSTYLE_POOL
2784 gnm_style_pool =
2785 go_mem_chunk_new ("style pool",
2786 sizeof (GnmStyle),
2787 16 * 1024 - 128);
2788 #endif
2791 #if USE_MSTYLE_POOL
2792 static void
2793 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2795 GnmStyle *style = data;
2796 g_printerr ("Leaking style at %p.\n", (void *)style);
2797 gnm_style_dump (style);
2799 #endif
2802 * gnm_style_shutdown: (skip)
2804 void
2805 gnm_style_shutdown (void)
2807 #if USE_MSTYLE_POOL
2808 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2809 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2810 gnm_style_pool = NULL;
2811 #endif