Update Spanish translation
[gnumeric.git] / src / mstyle.c
blob51d70a315c7f71bcc097853e15f032cec7dd3e7c
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->conditions != NULL, NULL);
2307 g_return_val_if_fail (ix >= 0 && (unsigned)ix < gnm_style_conditions_details (style->conditions)->len, NULL);
2309 if (style->changed)
2310 gnm_style_update ((GnmStyle *)style);
2312 return g_ptr_array_index (style->cond_styles, ix);
2316 static gboolean
2317 debug_style_deps (void)
2319 static int debug = -1;
2320 if (debug < 0)
2321 debug = gnm_debug_flag ("style-deps");
2322 return debug;
2326 * Just a simple version for now. We can also ignore most function
2327 * calls[1] and self-references[2].
2329 * [1] Excluding volatile (TODAY, ...) and those that can create references
2330 * outside the arguments (INDIRECT).
2332 * [2] References that print like A1 when used in A1.
2334 static gboolean
2335 cond_expr_harmless (GnmExpr const *expr)
2337 GnmValue const *v = gnm_expr_get_constant (expr);
2338 if (v && !VALUE_IS_CELLRANGE (v))
2339 return TRUE;
2341 return FALSE;
2345 void
2346 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
2348 GnmStyleConditions *sc;
2349 Sheet *sheet;
2351 g_return_if_fail (style != NULL);
2352 g_return_if_fail (r != NULL);
2354 sheet = style->linked_sheet;
2357 * Conditional formatting.
2359 * We need to trigger a reformatting of the cell if a cell referenced
2360 * by the condition changes.
2362 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2363 ? gnm_style_get_conditions (style)
2364 : NULL;
2365 if (sc) {
2366 GPtrArray const *conds = gnm_style_conditions_details (sc);
2367 guint ui;
2368 if (debug_style_deps ())
2369 g_printerr ("Linking %s for %p\n",
2370 range_as_string (r), style);
2371 for (ui = 0; conds && ui < conds->len; ui++) {
2372 GnmStyleCond const *c = g_ptr_array_index (conds, ui);
2373 guint ei;
2375 for (ei = 0; ei < 2; ei++) {
2376 GnmExprTop const *texpr =
2377 gnm_style_cond_get_expr (c, ei);
2378 if (!texpr ||
2379 cond_expr_harmless (texpr->expr))
2380 continue;
2381 if (!style->deps)
2382 style->deps = g_ptr_array_new ();
2383 gnm_dep_style_dependency
2384 (sheet, texpr, r, style->deps);
2390 * Validations.
2392 * We can probably ignore those. If a dependent cell changes such
2393 * that a validation condition is no longer satisfied, it is
2394 * grandfathered in as valid.
2397 /* The style owns the deps. */
2400 void
2401 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2403 unsigned ui, k;
2405 g_return_if_fail (style != NULL);
2406 g_return_if_fail (r != NULL);
2408 if (!style->deps)
2409 return;
2411 for (ui = k = 0; ui < style->deps->len; ui++) {
2412 GnmDependent *dep = g_ptr_array_index (style->deps, ui);
2413 GnmCellPos const *pos = dependent_pos (dep);
2415 if (range_contains (r, pos->col, pos->row)) {
2416 if (debug_style_deps ())
2417 g_printerr ("Unlinking %s for %p\n",
2418 cellpos_as_string (pos), style);
2419 dependent_set_expr (dep, NULL);
2420 g_free (dep);
2421 } else {
2422 g_ptr_array_index (style->deps, k) = dep;
2423 k++;
2427 g_ptr_array_set_size (style->deps, k);
2432 * gnm_style_visible_in_blank:
2433 * @style: style to query
2435 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2436 * that means if it has a background or a visible border.
2438 gboolean
2439 gnm_style_visible_in_blank (GnmStyle const *style)
2441 GnmStyleElement i;
2443 g_return_val_if_fail (style != NULL, FALSE);
2445 if (elem_is_set (style, MSTYLE_PATTERN) &&
2446 gnm_style_get_pattern (style) > 0)
2447 return TRUE;
2449 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2450 if (elem_is_set (style, i) &&
2451 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2452 return TRUE;
2454 return FALSE;
2457 static void
2458 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2460 attr->start_index = 0;
2461 attr->end_index = G_MAXINT;
2462 pango_attr_list_insert (attrs, attr);
2466 * gnm_style_generate_attrs:
2467 * @style: style to query
2469 * Returns: (transfer full): a #PangoAttrList with attributes matching
2470 * @style. Attributes where the default will serve are not included.
2471 * The foreground color is not included.
2473 PangoAttrList *
2474 gnm_style_get_pango_attrs (GnmStyle const *style,
2475 PangoContext *context,
2476 double zoom)
2478 PangoAttrList *l;
2479 GnmUnderline ul;
2480 GnmFont *font = gnm_style_get_font (style, context);
2482 if (style->pango_attrs) {
2483 if (zoom == style->pango_attrs_zoom) {
2484 pango_attr_list_ref (style->pango_attrs);
2485 return style->pango_attrs;
2487 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2490 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2491 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2492 ((GnmStyle *)style)->pango_attrs_height = -1;
2494 /* Foreground colour. */
2495 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2496 if (0) {
2497 GnmColor const *fore = style->color.font;
2498 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2501 /* Handle underlining. */
2502 ul = gnm_style_get_font_uline (style);
2503 if (ul != UNDERLINE_NONE)
2504 add_attr (l,
2505 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2507 /* Handle strikethrough. */
2508 if (gnm_style_get_font_strike (style))
2509 add_attr (l, pango_attr_strikethrough_new (TRUE));
2511 /* Handle script. */
2512 switch (gnm_style_get_font_script (style)) {
2513 default:
2514 case GO_FONT_SCRIPT_STANDARD:
2515 break;
2516 case GO_FONT_SCRIPT_SUB:
2517 add_attr (l, go_pango_attr_subscript_new (TRUE));
2518 break;
2519 case GO_FONT_SCRIPT_SUPER:
2520 add_attr (l, go_pango_attr_superscript_new (TRUE));
2521 break;
2524 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2526 if (zoom != 1)
2527 add_attr (l, pango_attr_scale_new (zoom));
2529 pango_attr_list_ref (l);
2530 return l;
2534 * gnm_style_generate_attrs_full:
2535 * @style: style to query
2537 * Returns: (transfer full): a #PangoAttrList with attributes matching
2538 * @style, even attributes where the default would have served.
2540 PangoAttrList *
2541 gnm_style_generate_attrs_full (GnmStyle const *style)
2543 GnmColor const *fore = style->color.font;
2544 PangoAttrList *l = pango_attr_list_new ();
2546 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2547 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2548 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2549 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2550 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2551 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2552 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2553 add_attr (l, pango_attr_strikethrough_new
2554 (gnm_style_get_font_strike (style)));
2555 add_attr (l, pango_attr_underline_new
2556 (gnm_translate_underline_to_pango
2557 (gnm_style_get_font_uline (style))));
2558 return l;
2562 gnm_style_get_pango_height (GnmStyle const *style,
2563 PangoContext *context,
2564 double zoom)
2566 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2568 if (style->pango_attrs_height == -1) {
2569 int h;
2570 PangoLayout *layout = pango_layout_new (context);
2571 GOFormat const *fmt;
2572 gboolean requires_translation = FALSE;
2574 fmt = gnm_style_get_format (style);
2575 if (!go_format_is_general (fmt)) {
2576 GOFormatDetails details;
2577 go_format_get_details (fmt, &details, NULL);
2578 if (details.family == GO_FORMAT_SCIENTIFIC &&
2579 details.use_markup) {
2580 PangoAttribute *a
2581 = go_pango_attr_superscript_new (TRUE);
2582 /* We want to superscript the "-01" in the */
2583 /* string "+1.23456789E-01" */
2584 a->start_index = 12;
2585 a->end_index = 15;
2586 pango_attr_list_insert (attrs, a);
2587 requires_translation = TRUE;
2590 pango_layout_set_attributes (layout, attrs);
2591 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2592 if (requires_translation)
2593 go_pango_translate_layout (layout);
2594 pango_layout_get_pixel_size (layout, NULL, &h);
2595 g_object_unref (layout);
2596 ((GnmStyle *)style)->pango_attrs_height = h;
2599 pango_attr_list_unref (attrs);
2600 return style->pango_attrs_height;
2604 void
2605 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2607 switch (attr->klass->type) {
2608 case PANGO_ATTR_FAMILY:
2609 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2610 break;
2611 case PANGO_ATTR_SIZE:
2612 gnm_style_set_font_size (style,
2613 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2614 break;
2615 case PANGO_ATTR_STYLE:
2616 gnm_style_set_font_italic (style,
2617 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2618 break;
2619 case PANGO_ATTR_WEIGHT:
2620 gnm_style_set_font_bold (style,
2621 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2622 break;
2623 case PANGO_ATTR_FOREGROUND:
2624 gnm_style_set_font_color (style, gnm_color_new_pango (
2625 &((PangoAttrColor *)attr)->color));
2626 break;
2627 case PANGO_ATTR_UNDERLINE:
2628 gnm_style_set_font_uline
2629 (style, gnm_translate_underline_from_pango
2630 (((PangoAttrInt *)attr)->value));
2631 break;
2632 case PANGO_ATTR_STRIKETHROUGH:
2633 gnm_style_set_font_strike (style,
2634 ((PangoAttrInt *)attr)->value != 0);
2635 break;
2636 default : {
2637 gboolean script_seen = FALSE, script_set = FALSE;
2638 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2639 script_seen = TRUE;
2640 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2641 script_set = TRUE;
2642 gnm_style_set_font_script
2643 (style, GO_FONT_SCRIPT_SUPER);
2645 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2646 script_seen = TRUE;
2647 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2648 script_set = TRUE;
2649 gnm_style_set_font_script
2650 (style, GO_FONT_SCRIPT_SUB);
2653 if (script_seen && !script_set)
2654 gnm_style_set_font_script
2655 (style, GO_FONT_SCRIPT_STANDARD);
2656 break; /* ignored */
2661 /* ------------------------------------------------------------------------- */
2663 static void
2664 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2666 if (color)
2667 g_printerr ("\t%s: %x:%x:%x%s\n",
2668 gnm_style_element_name [elem],
2669 GO_COLOR_UINT_R (color->go_color),
2670 GO_COLOR_UINT_G (color->go_color),
2671 GO_COLOR_UINT_B (color->go_color),
2672 color->is_auto ? " auto" : "");
2673 else
2674 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2677 static void
2678 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2680 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2681 if (border)
2682 g_printerr ("%d\n", border->line_type);
2683 else
2684 g_printerr ("blank\n");
2688 * gnm_style_dump:
2689 * @style: style to dump
2691 * This function dumps the given style's contents to stderr. This is meant
2692 * for debug purposes only and doesn't do a very good job for, for example,
2693 * conditional style settings.
2695 void
2696 gnm_style_dump (GnmStyle const *style)
2698 int i;
2700 g_printerr ("Style Refs %d\n", style->ref_count);
2701 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2702 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2703 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2704 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2706 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2707 if (elem_is_set (style, i))
2708 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2710 if (elem_is_set (style, MSTYLE_PATTERN))
2711 g_printerr ("\tpattern %d\n", style->pattern);
2712 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2713 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2714 if (elem_is_set (style, MSTYLE_FONT_NAME))
2715 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2716 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2717 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2718 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2719 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2720 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2721 switch (style->font_detail.underline) {
2722 default:
2723 case UNDERLINE_NONE:
2724 g_printerr ("\tno underline\n"); break;
2725 case UNDERLINE_SINGLE:
2726 g_printerr ("\tsingle underline\n"); break;
2727 case UNDERLINE_DOUBLE:
2728 g_printerr ("\tdouble underline\n"); break;
2730 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2731 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2732 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2733 switch (style->font_detail.script) {
2734 case GO_FONT_SCRIPT_SUB:
2735 g_printerr ("\tsubscript\n"); break;
2736 default:
2737 case GO_FONT_SCRIPT_STANDARD:
2738 g_printerr ("\tno super or sub\n"); break;
2739 case GO_FONT_SCRIPT_SUPER:
2740 g_printerr ("\tsuperscript\n"); break;
2742 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2743 g_printerr ("\tsize %f\n", style->font_detail.size);
2744 if (elem_is_set (style, MSTYLE_FORMAT)) {
2745 const char *fmt = go_format_as_XL (style->format);
2746 g_printerr ("\tformat '%s'\n", fmt);
2748 if (elem_is_set (style, MSTYLE_ALIGN_V))
2749 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2750 if (elem_is_set (style, MSTYLE_ALIGN_H))
2751 g_printerr ("\thalign %hd\n", (short)style->h_align);
2752 if (elem_is_set (style, MSTYLE_INDENT))
2753 g_printerr ("\tindent %d\n", style->indent);
2754 if (elem_is_set (style, MSTYLE_ROTATION))
2755 g_printerr ("\trotation %d\n", style->rotation);
2756 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2757 g_printerr ("\ttext dir %d\n", style->text_dir);
2758 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2759 g_printerr ("\twrap text %d\n", style->wrap_text);
2760 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2761 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2762 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2763 g_printerr ("\tlocked %d\n", style->contents_locked);
2764 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2765 g_printerr ("\thidden %d\n", style->contents_hidden);
2766 if (elem_is_set (style, MSTYLE_VALIDATION))
2767 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2768 if (elem_is_set (style, MSTYLE_HLINK))
2769 g_printerr ("\thlink %p\n", (void *)style->hlink);
2770 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2771 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2772 if (elem_is_set (style, MSTYLE_CONDITIONS))
2773 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2776 /* ------------------------------------------------------------------------- */
2779 * gnm_style_init: (skip)
2781 void
2782 gnm_style_init (void)
2784 #if USE_MSTYLE_POOL
2785 gnm_style_pool =
2786 go_mem_chunk_new ("style pool",
2787 sizeof (GnmStyle),
2788 16 * 1024 - 128);
2789 #endif
2792 #if USE_MSTYLE_POOL
2793 static void
2794 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2796 GnmStyle *style = data;
2797 g_printerr ("Leaking style at %p.\n", (void *)style);
2798 gnm_style_dump (style);
2800 #endif
2803 * gnm_style_shutdown: (skip)
2805 void
2806 gnm_style_shutdown (void)
2808 #if USE_MSTYLE_POOL
2809 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2810 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2811 gnm_style_pool = NULL;
2812 #endif