1 /*
2 * mstyle.c: Storing a style
3 *
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>
13
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 <sheet-conditions.h>
20 #include <validation.h>
21 #include <pattern.h>
22 #include <hlink.h>
23 #include <input-msg.h>
24 #include <application.h>
25 #include <parse-util.h>
26 #include <expr.h>
27 #include <value.h>
28 #include <gutils.h>
29 #include <ranges.h>
30 #include <sheet.h>
31 #include <gnumeric-conf.h>
32 #include <goffice/goffice.h>
33 #include <string.h>
34
35 static gboolean debug_style_deps;
36
37 #define DEBUG_STYLES
38 #ifndef USE_MSTYLE_POOL
39 #define USE_MSTYLE_POOL 1
40 #endif
41
42 #if USE_MSTYLE_POOL
43 /* Memory pool for GnmStyles. */
44 static GOMemChunk *gnm_style_pool;
45 #define CHUNK_ALLOC(T,p) ((T*)go_mem_chunk_alloc (p))
46 #define CHUNK_ALLOC0(T,p) ((T*)go_mem_chunk_alloc0 (p))
47 #define CHUNK_FREE(p,v) go_mem_chunk_free ((p), (v))
48 #else
49 #define CHUNK_ALLOC(T,c) g_new (T,1)
50 #define CHUNK_ALLOC0(T,c) g_new0 (T,1)
51 #define CHUNK_FREE(p,v) g_free ((v))
52 #endif
53
54
55 struct _GnmStyle {
56 unsigned int changed;
57 unsigned int set;
58
59 unsigned int hash_key;
60 unsigned int hash_key_xl;
61 unsigned int ref_count;
62 unsigned int link_count;
63 Sheet *linked_sheet;
64
65 PangoAttrList *pango_attrs;
66 double pango_attrs_zoom;
67 int pango_attrs_height;
68
69 GnmFont *font;
70 PangoContext *font_context;
71
72 /* public */
73 struct _GnmStyleColor {
74 GnmColor *font;
75 GnmColor *back;
76 GnmColor *pattern;
77 } color;
78 GnmBorder *borders[MSTYLE_BORDER_DIAGONAL - MSTYLE_BORDER_TOP + 1];
79 guint32 pattern;
80
81 /* FIXME: TODO use GOFont */
82 struct _GnmStyleFontDetails {
83 GOString *name;
84 gboolean bold;
85 gboolean italic;
86 GnmUnderline underline;
87 gboolean strikethrough;
88 GOFontScript script;
89 double size;
90 } font_detail;
91
92 GOFormat const *format;
93 GnmHAlign h_align;
94 GnmVAlign v_align;
95 int indent;
96 int rotation;
97 int text_dir;
98 gboolean wrap_text;
99 gboolean shrink_to_fit;
100 gboolean contents_locked;
101 gboolean contents_hidden;
102
103 GnmValidation *validation;
104 GnmHLink *hlink;
105 GnmInputMsg *input_msg;
106 GnmStyleConditions *conditions;
107 GPtrArray *cond_styles;
108 };
109
110 #define elem_changed(style, elem) do { (style)->changed |= (1u << (elem)); } while(0)
111 #define elem_set(style, elem) do { (style)->set |= (1u << (elem)); } while(0)
112 #define elem_unset(style, elem) do { (style)->set &= ~(1u << (elem)); } while(0)
113 #define elem_is_set(style, elem) (((style)->set & (1u << (elem))) != 0)
114
115 #define MSTYLE_ANY_BORDER MSTYLE_BORDER_TOP: \
116 case MSTYLE_BORDER_BOTTOM: \
117 case MSTYLE_BORDER_LEFT: \
118 case MSTYLE_BORDER_RIGHT: \
119 case MSTYLE_BORDER_DIAGONAL: \
120 case MSTYLE_BORDER_REV_DIAGONAL
121
122
123 #define UNROLLED_FOR(init_,cond_,step_,code_) \
124 do { \
125 init_; \
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 if (cond_) { code_; step_; \
175 if (cond_) { code_; step_; \
176 g_assert_not_reached (); \
177 }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} \
178 } while (0)
179
180
181
182 static char const * const
183 gnm_style_element_name[MSTYLE_ELEMENT_MAX] = {
184 "Color.Back",
185 "Color.Pattern",
186 "Border.Top",
187 "Border.Bottom",
188 "Border.Left",
189 "Border.Right",
190 "Border.RevDiagonal",
191 "Border.Diagonal",
192 "Pattern",
193 "Color.Fore",
194 "Font.Name",
195 "Font.Bold",
196 "Font.Italic",
197 "Font.Underline",
198 "Font.Strikethrough",
199 "Font.Script",
200 "Font.Size",
201 "Format",
202 "Align.v",
203 "Align.h",
204 "Indent",
205 "Rotation",
206 "WrapText",
207 "ShrinkToFit",
208 "Contents.Locked",
209 "Contents.Hidden",
210 "Validation",
211 "Hyper Link",
212 "Input Msg"
213 };
214
215 /* Some ref/link count debugging */
216 #if 0
217 #define d(arg) g_printerr arg
218 #else
219 #define d(arg) do { } while (0)
220 #endif
221
222 static void
clear_conditional_merges(GnmStyle * style)223 clear_conditional_merges (GnmStyle *style)
224 {
225 if (style->cond_styles) {
226 unsigned i = style->cond_styles->len;
227 while (i-- > 0)
228 gnm_style_unref (g_ptr_array_index (style->cond_styles, i));
229 g_ptr_array_free (style->cond_styles, TRUE);
230 style->cond_styles = NULL;
231 }
232 }
233
234 #define MIX(H) do { \
235 H *= G_GUINT64_CONSTANT(123456789012345); \
236 H ^= (H >> 31); \
237 } while (0)
238
239 static void
gnm_style_update(GnmStyle * style)240 gnm_style_update (GnmStyle *style)
241 {
242 guint64 hash = 0;
243 int i;
244
245 g_return_if_fail (style->changed);
246
247 style->changed = 0;
248
249 clear_conditional_merges (style);
250 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
251 style->cond_styles = gnm_style_conditions_overlay (style->conditions, style);
252
253 /* ---------------------------------------- */
254
255 if (elem_is_set (style, MSTYLE_COLOR_BACK)) {
256 if (!style->color.back->is_auto)
257 hash ^= GPOINTER_TO_UINT (style->color.back);
258 else
259 hash++;
260 }
261 MIX (hash);
262
263 if (elem_is_set (style, MSTYLE_COLOR_PATTERN)) {
264 if (!style->color.pattern->is_auto)
265 hash ^= GPOINTER_TO_UINT (style->color.pattern);
266 else
267 hash++;
268 }
269 MIX (hash);
270
271 if (elem_is_set (style, MSTYLE_FONT_COLOR)) {
272 if (!style->color.font->is_auto)
273 hash ^= GPOINTER_TO_UINT (style->color.font);
274 else
275 hash++;
276 }
277 MIX (hash);
278
279 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; i++) {
280 if (elem_is_set (style, i))
281 hash ^= GPOINTER_TO_UINT (style->borders[i - MSTYLE_BORDER_TOP]);
282 else
283 hash++;
284 MIX (hash);
285 }
286
287 if (elem_is_set (style, MSTYLE_PATTERN))
288 hash ^= style->pattern;
289 MIX (hash);
290
291 if (elem_is_set (style, MSTYLE_FONT_NAME))
292 hash ^= GPOINTER_TO_UINT (style->font_detail.name);
293 MIX (hash);
294
295 if (elem_is_set (style, MSTYLE_FONT_BOLD))
296 hash ^= (style->font_detail.bold ? 1 : 2);
297 MIX (hash);
298
299 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
300 hash ^= (style->font_detail.italic ? 1 : 2);
301 MIX (hash);
302
303 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
304 hash ^= (style->font_detail.underline ? 1 : 2);
305 MIX (hash);
306
307 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
308 hash ^= (style->font_detail.strikethrough ? 1 : 2);
309 MIX (hash);
310
311 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
312 hash ^= (style->font_detail.script + 0x100);
313 MIX (hash);
314
315 if (elem_is_set (style, MSTYLE_FONT_SIZE))
316 hash ^= ((int)(style->font_detail.size * 97));
317 MIX (hash);
318
319 if (elem_is_set (style, MSTYLE_FORMAT))
320 hash ^= GPOINTER_TO_UINT (style->format);
321 MIX (hash);
322
323 if (elem_is_set (style, MSTYLE_ALIGN_H))
324 hash ^= (style->h_align + 0x100);
325 MIX (hash);
326
327 if (elem_is_set (style, MSTYLE_ALIGN_V))
328 hash ^= (style->v_align + 0x100);
329 MIX (hash);
330
331 if (elem_is_set (style, MSTYLE_INDENT))
332 hash ^= style->indent;
333 MIX (hash);
334
335 if (elem_is_set (style, MSTYLE_ROTATION))
336 hash ^= style->rotation;
337 MIX (hash);
338
339 if (elem_is_set (style, MSTYLE_TEXT_DIR))
340 hash ^= (style->text_dir + 0x100);
341 MIX (hash);
342
343 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
344 hash ^= (style->wrap_text ? 1 : 2);
345 MIX (hash);
346
347 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
348 hash ^= (style->shrink_to_fit ? 1 : 2);
349 MIX (hash);
350
351 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
352 hash ^= (style->contents_locked ? 1 : 2);
353 MIX (hash);
354
355 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
356 hash ^= (style->contents_hidden ? 1 : 2);
357 MIX (hash);
358
359 style->hash_key_xl = (guint32)hash;
360
361 /* From here on, fields are not in MS XL */
362
363 if (elem_is_set (style, MSTYLE_VALIDATION)) {
364 /*
365 * The hash used must not depend on the expressions inside
366 * the validation.
367 */
368 hash ^= (style->validation != NULL ? 1 : 2);
369 }
370 MIX (hash);
371
372 if (elem_is_set (style, MSTYLE_HLINK))
373 hash ^= GPOINTER_TO_UINT (style->hlink);
374 MIX (hash);
375
376 if (elem_is_set (style, MSTYLE_INPUT_MSG))
377 hash ^= GPOINTER_TO_UINT (style->input_msg);
378 MIX (hash);
379
380 if (elem_is_set (style, MSTYLE_CONDITIONS)) {
381 /*
382 * The hash used must not depend on the expressions inside
383 * the conditions.
384 */
385 hash ^= style->conditions
386 ? gnm_style_conditions_hash (style->conditions)
387 : 1u;
388 }
389 MIX (hash);
390
391 style->hash_key = (guint32)hash;
392
393 if (G_UNLIKELY (style->set == 0)) {
394 /*
395 * gnm_style_new and gnm_style_dup both assume that the
396 * correct hash values (both of them) for the empty style
397 * is zero.
398 */
399 g_assert (style->hash_key == 0);
400 g_assert (style->hash_key_xl == 0);
401 }
402 }
403
404 #undef MIX
405
406 guint
gnm_style_hash_XL(gconstpointer style)407 gnm_style_hash_XL (gconstpointer style)
408 {
409 if (((GnmStyle const *)style)->changed)
410 gnm_style_update ((GnmStyle *)style);
411 return ((GnmStyle const *)style)->hash_key_xl;
412 }
413
414 guint
gnm_style_hash(gconstpointer style)415 gnm_style_hash (gconstpointer style)
416 {
417 if (((GnmStyle const *)style)->changed)
418 gnm_style_update ((GnmStyle *)style);
419 return ((GnmStyle const *)style)->hash_key;
420 }
421
422 #define ELEM_IS_EQ(a,b,elem) \
423 (elem == MSTYLE_COLOR_BACK \
424 ? a->color.back == b->color.back || (a->color.back->is_auto && b->color.back->is_auto) \
425 : (elem == MSTYLE_COLOR_PATTERN \
426 ? a->color.pattern == b->color.pattern || (a->color.pattern->is_auto && b->color.pattern->is_auto) \
427 : (elem >= MSTYLE_BORDER_TOP && elem <= MSTYLE_BORDER_DIAGONAL) \
428 ? a->borders[elem - MSTYLE_BORDER_TOP] == b->borders[elem - MSTYLE_BORDER_TOP] \
429 : (elem == MSTYLE_PATTERN \
430 ? a->pattern == b->pattern \
431 : (elem == MSTYLE_FONT_COLOR \
432 ? a->color.font == b->color.font || (a->color.font->is_auto && b->color.font->is_auto) \
433 : (elem == MSTYLE_FONT_NAME \
434 ? a->font_detail.name == b->font_detail.name \
435 : (elem == MSTYLE_FONT_BOLD \
436 ? a->font_detail.bold == b->font_detail.bold \
437 : (elem == MSTYLE_FONT_ITALIC \
438 ? a->font_detail.italic == b->font_detail.italic \
439 : (elem == MSTYLE_FONT_UNDERLINE \
440 ? a->font_detail.underline == b->font_detail.underline \
441 : (elem == MSTYLE_FONT_STRIKETHROUGH \
442 ? a->font_detail.strikethrough == b->font_detail.strikethrough \
443 : (elem == MSTYLE_FONT_SCRIPT \
444 ? a->font_detail.script == b->font_detail.script \
445 : (elem == MSTYLE_FONT_SIZE \
446 ? a->font_detail.size == b->font_detail.size \
447 : (elem == MSTYLE_FORMAT \
448 ? a->format == b->format \
449 : (elem == MSTYLE_ALIGN_V \
450 ? a->v_align == b->v_align \
451 : (elem == MSTYLE_ALIGN_H \
452 ? a->h_align == b->h_align \
453 : (elem == MSTYLE_INDENT \
454 ? a->indent == b->indent \
455 : (elem == MSTYLE_ROTATION \
456 ? a->rotation == b->rotation \
457 : (elem == MSTYLE_TEXT_DIR \
458 ? a->text_dir == b->text_dir \
459 : (elem == MSTYLE_WRAP_TEXT \
460 ? a->wrap_text == b->wrap_text \
461 : (elem == MSTYLE_SHRINK_TO_FIT \
462 ? a->shrink_to_fit == b->shrink_to_fit \
463 : (elem == MSTYLE_CONTENTS_LOCKED \
464 ? a->contents_locked == b->contents_locked \
465 : (elem == MSTYLE_CONTENTS_HIDDEN \
466 ? a->contents_hidden == b->contents_hidden \
467 : (elem == MSTYLE_VALIDATION \
468 ? a->validation == b->validation \
469 : (elem == MSTYLE_HLINK \
470 ? a->hlink == b->hlink \
471 : (elem == MSTYLE_INPUT_MSG \
472 ? a->input_msg == b->input_msg \
473 : (elem == MSTYLE_CONDITIONS \
474 ? (a->conditions == b->conditions || \
475 (a->conditions && b->conditions && \
476 gnm_style_conditions_equal (a->conditions, b->conditions, FALSE))) \
477 : FALSE)))))))))))))))))))))))))
478
479 /*
480 * Note: the above is suboptimal for validation, hlink, input_msg.
481 *
482 * We are comparing pointers (which at least safely matches what we do
483 * with the hash), but I think we want proper equality.
484 */
485
486 static gboolean
elem_is_eq(GnmStyle const * a,GnmStyle const * b,GnmStyleElement elem)487 elem_is_eq (GnmStyle const *a, GnmStyle const *b, GnmStyleElement elem)
488 {
489 return ELEM_IS_EQ (a, b, elem);
490 }
491
492 static void
elem_assign_contents(GnmStyle * dst,GnmStyle const * src,GnmStyleElement elem)493 elem_assign_contents (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
494 {
495 #ifdef DEBUG_STYLES
496 g_return_if_fail (src != dst);
497 g_return_if_fail (elem_is_set (src, elem));
498 #endif
499 switch (elem) {
500 case MSTYLE_COLOR_BACK : style_color_ref (dst->color.back = src->color.back); return;
501 case MSTYLE_COLOR_PATTERN : style_color_ref (dst->color.pattern = src->color.pattern); return;
502 case MSTYLE_ANY_BORDER:
503 elem -= MSTYLE_BORDER_TOP;
504 gnm_style_border_ref (dst->borders[elem] = src->borders[elem]);
505 return;
506 case MSTYLE_PATTERN: dst->pattern = src->pattern; return;
507 case MSTYLE_FONT_COLOR : style_color_ref (dst->color.font = src->color.font); return;
508 case MSTYLE_FONT_NAME: go_string_ref (dst->font_detail.name = src->font_detail.name); return;
509 case MSTYLE_FONT_BOLD: dst->font_detail.bold = src->font_detail.bold; return;
510 case MSTYLE_FONT_ITALIC: dst->font_detail.italic = src->font_detail.italic; return;
511 case MSTYLE_FONT_UNDERLINE: dst->font_detail.underline = src->font_detail.underline; return;
512 case MSTYLE_FONT_STRIKETHROUGH: dst->font_detail.strikethrough = src->font_detail.strikethrough; return;
513 case MSTYLE_FONT_SCRIPT: dst->font_detail.script = src->font_detail.script; return;
514 case MSTYLE_FONT_SIZE: dst->font_detail.size = src->font_detail.size; return;
515 case MSTYLE_FORMAT: go_format_ref (dst->format = src->format); return;
516 case MSTYLE_ALIGN_V: dst->v_align = src->v_align; return;
517 case MSTYLE_ALIGN_H: dst->h_align = src->h_align; return;
518 case MSTYLE_INDENT: dst->indent = src->indent; return;
519 case MSTYLE_ROTATION: dst->rotation = src->rotation; return;
520 case MSTYLE_TEXT_DIR: dst->text_dir = src->text_dir; return;
521 case MSTYLE_WRAP_TEXT: dst->wrap_text = src->wrap_text; return;
522 case MSTYLE_SHRINK_TO_FIT: dst->shrink_to_fit = src->shrink_to_fit; return;
523 case MSTYLE_CONTENTS_LOCKED: dst->contents_locked = src->contents_locked; return;
524 case MSTYLE_CONTENTS_HIDDEN: dst->contents_hidden = src->contents_hidden; return;
525 case MSTYLE_VALIDATION:
526 if ((dst->validation = src->validation))
527 gnm_validation_ref (dst->validation);
528 return;
529 case MSTYLE_HLINK:
530 if ((dst->hlink = src->hlink))
531 g_object_ref (dst->hlink);
532 return;
533 case MSTYLE_INPUT_MSG:
534 if ((dst->input_msg = src->input_msg))
535 g_object_ref (dst->input_msg);
536 return;
537 case MSTYLE_CONDITIONS:
538 if ((dst->conditions = src->conditions))
539 g_object_ref (dst->conditions);
540 return;
541 default:
542 ;
543 }
544 }
545
546 static void
elem_clear_contents(GnmStyle * style,GnmStyleElement elem)547 elem_clear_contents (GnmStyle *style, GnmStyleElement elem)
548 {
549 #ifdef DEBUG_STYLES
550 g_return_if_fail (style != NULL);
551 #endif
552 if (!elem_is_set (style, elem))
553 return;
554
555 switch (elem) {
556 case MSTYLE_COLOR_BACK : style_color_unref (style->color.back); return;
557 case MSTYLE_COLOR_PATTERN : style_color_unref (style->color.pattern); return;
558 case MSTYLE_ANY_BORDER:
559 gnm_style_border_unref (style->borders[elem - MSTYLE_BORDER_TOP]);
560 return;
561 case MSTYLE_FONT_COLOR : style_color_unref (style->color.font); return;
562 case MSTYLE_FONT_NAME: go_string_unref (style->font_detail.name); return;
563 case MSTYLE_FORMAT: go_format_unref (style->format); return;
564 case MSTYLE_VALIDATION:
565 if (style->validation)
566 gnm_validation_unref (style->validation);
567 return;
568 case MSTYLE_HLINK:
569 if (style->hlink)
570 g_object_unref (style->hlink);
571 return;
572 case MSTYLE_INPUT_MSG:
573 if (style->input_msg)
574 g_object_unref (style->input_msg);
575 return;
576 case MSTYLE_CONDITIONS:
577 if (style->conditions) {
578 clear_conditional_merges (style);
579 g_object_unref (style->conditions);
580 }
581 return;
582 default:
583 ;
584 }
585 }
586
587 /**
588 * gnm_style_find_conflicts:
589 * @accum: accumulator #GnmStyle
590 * @overlay: #GnmStyle
591 * @conflicts: flags
592 *
593 * Copy any items from @overlay that do not conflict with the values in @accum.
594 * If an element had a previous conflict (flagged via @conflicts) it is ignored.
595 *
596 * Returns @conflicts with any new conflicts added.
597 **/
598 unsigned int
gnm_style_find_conflicts(GnmStyle * accum,GnmStyle const * overlay,unsigned int conflicts)599 gnm_style_find_conflicts (GnmStyle *accum, GnmStyle const *overlay,
600 unsigned int conflicts)
601 {
602 int i;
603
604 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (conflicts));
605
606 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
607 if (conflicts & (1u << i) || !elem_is_set (overlay, i)) {
608 /* Nothing */
609 } else if (!elem_is_set (accum, i)) {
610 elem_assign_contents (accum, overlay, i);
611 elem_set (accum, i);
612 elem_changed (accum, i);
613 } else if (!elem_is_eq (accum, overlay, i))
614 conflicts |= (1u << i);
615 }
616
617 return conflicts;
618 }
619
620 #define GNM_INPUT_MSG_EQUAL3(a,b,r) (gnm_input_msg_equal (a,b))
621
622 #define RELAX_CHECK(op_,field_,checker_) do { \
623 if (diffs & (1u << (op_)) && \
624 elem_is_set (a, (op_)) && \
625 elem_is_set (b, (op_)) && \
626 ((a->field_ == NULL) != (b->field_ == NULL) || \
627 checker_ (a->field_, b->field_, relax_sheet))) \
628 diffs &= ~(1u << (op_)); \
629 } while (0)
630
631 /**
632 * gnm_style_find_differences:
633 * @a: A #GnmStyle
634 * @b: A #GnmStyle
635 * @relax_sheet: if %TRUE, ignore differences solely caused by being linked into different sheets.
636 *
637 * Determine how two fully-qualified styles differ.
638 *
639 * Returns differences as a bitset of #GnmStyleElement.
640 **/
641 unsigned int
gnm_style_find_differences(GnmStyle const * a,GnmStyle const * b,gboolean relax_sheet)642 gnm_style_find_differences (GnmStyle const *a, GnmStyle const *b,
643 gboolean relax_sheet)
644 {
645 int i;
646 unsigned int diffs = 0;
647
648 g_assert (MSTYLE_ELEMENT_MAX <= CHAR_BIT * sizeof (diffs));
649
650 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
651 if (elem_is_set (a, i) != elem_is_set (b, i) ||
652 (elem_is_set (a, i) && !elem_is_eq (a, b, i)))
653 diffs |= (1u << i);
654 }
655
656 if (relax_sheet) {
657 RELAX_CHECK (MSTYLE_HLINK, hlink, gnm_hlink_equal);
658 RELAX_CHECK (MSTYLE_VALIDATION, validation, gnm_validation_equal);
659 RELAX_CHECK (MSTYLE_INPUT_MSG, input_msg, GNM_INPUT_MSG_EQUAL3);
660 RELAX_CHECK (MSTYLE_CONDITIONS, conditions, gnm_style_conditions_equal);
661 }
662
663 return diffs;
664 }
665
666 #undef RELAX_CHECK
667 #undef GNM_INPUT_MSG_EQUAL3
668
669 static inline void
gnm_style_clear_pango(GnmStyle * style)670 gnm_style_clear_pango (GnmStyle *style)
671 {
672 if (style->pango_attrs) {
673 pango_attr_list_unref (style->pango_attrs);
674 style->pango_attrs = NULL;
675 }
676 }
677
678
679 static inline void
gnm_style_clear_font(GnmStyle * style)680 gnm_style_clear_font (GnmStyle *style)
681 {
682 if (style->font) {
683 gnm_font_unref (style->font);
684 style->font = NULL;
685 }
686 g_clear_object (&style->font_context);
687 }
688
689 /**
690 * gnm_style_new:
691 *
692 * Returns: (transfer full): a new style with _no_ elements set.
693 **/
694 GnmStyle *
gnm_style_new(void)695 gnm_style_new (void)
696 {
697 GnmStyle *style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
698
699 style->ref_count = 1;
700 style->link_count = 0;
701 style->linked_sheet = NULL;
702 style->pango_attrs = NULL;
703 style->font = NULL;
704 style->validation = NULL;
705
706 style->set = style->changed = 0;
707 style->validation = NULL;
708 style->hlink = NULL;
709 style->input_msg = NULL;
710 style->conditions = NULL;
711
712 d(("new %p\n", style));
713
714 return style;
715 }
716
717 /**
718 * gnm_style_new_default:
719 *
720 * Returns: (transfer full): a new style initialized to the default state.
721 **/
722 GnmStyle *
gnm_style_new_default(void)723 gnm_style_new_default (void)
724 {
725 GnmStyle *new_style = gnm_style_new ();
726 int i;
727
728 gnm_style_set_font_name (new_style, gnm_conf_get_core_defaultfont_name ());
729 gnm_style_set_font_size (new_style, gnm_conf_get_core_defaultfont_size ());
730 gnm_style_set_font_bold (new_style, gnm_conf_get_core_defaultfont_bold ());
731 gnm_style_set_font_italic (new_style, gnm_conf_get_core_defaultfont_italic ());
732
733 gnm_style_set_format (new_style, go_format_general ());
734 gnm_style_set_align_v (new_style, GNM_VALIGN_BOTTOM);
735 gnm_style_set_align_h (new_style, GNM_HALIGN_GENERAL);
736 gnm_style_set_indent (new_style, 0);
737 gnm_style_set_rotation (new_style, 0);
738 gnm_style_set_text_dir (new_style, GNM_TEXT_DIR_CONTEXT);
739 gnm_style_set_wrap_text (new_style, FALSE);
740 gnm_style_set_shrink_to_fit (new_style, FALSE);
741 gnm_style_set_contents_locked (new_style, TRUE);
742 gnm_style_set_contents_hidden (new_style, FALSE);
743 gnm_style_set_font_uline (new_style, UNDERLINE_NONE);
744 gnm_style_set_font_strike (new_style, FALSE);
745 gnm_style_set_font_script (new_style, GO_FONT_SCRIPT_STANDARD);
746
747 gnm_style_set_validation (new_style, NULL);
748 gnm_style_set_hlink (new_style, NULL);
749 gnm_style_set_input_msg (new_style, NULL);
750 gnm_style_set_conditions (new_style, NULL);
751
752 gnm_style_set_font_color (new_style, style_color_black ());
753 gnm_style_set_back_color (new_style, style_color_auto_back ());
754 gnm_style_set_pattern_color (new_style, style_color_black ());
755
756 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
757 gnm_style_set_border (new_style, i,
758 gnm_style_border_ref (gnm_style_border_none ()));
759 gnm_style_set_pattern (new_style, 0);
760
761 return new_style;
762 }
763
764 GnmStyle *
gnm_style_dup(GnmStyle const * src)765 gnm_style_dup (GnmStyle const *src)
766 {
767 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
768 int i;
769
770 new_style->ref_count = 1;
771 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
772 if (elem_is_set (src, i)) {
773 elem_assign_contents (new_style, src, i);
774 elem_set (new_style, i);
775 elem_changed (new_style, i);
776 }
777
778 if ((new_style->pango_attrs = src->pango_attrs)) {
779 pango_attr_list_ref (new_style->pango_attrs);
780 new_style->pango_attrs_zoom = src->pango_attrs_zoom;
781 }
782
783 if ((new_style->font = src->font)) {
784 gnm_font_ref (new_style->font);
785 new_style->font_context = g_object_ref (src->font_context);
786 }
787
788 d(("dup %p\n", new_style));
789 return new_style;
790 }
791
792 /**
793 * gnm_style_new_merged:
794 * @base: #GnmStyle
795 * @overlay: #GnmStyle
796 *
797 * A new GnmStyle that contains any elements of @overlay that are set, and uses
798 * @base for anything that is not set in @overlay.
799 *
800 * Returns: (transfer full): A ref to a new GnmStyle.
801 **/
802 GnmStyle *
gnm_style_new_merged(GnmStyle const * base,GnmStyle const * overlay)803 gnm_style_new_merged (GnmStyle const *base, GnmStyle const *overlay)
804 {
805 GnmStyle *new_style = CHUNK_ALLOC0 (GnmStyle, gnm_style_pool);
806 int i;
807
808 new_style->ref_count = 1;
809 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++) {
810 if (elem_is_set (overlay, i))
811 elem_assign_contents (new_style, overlay, i);
812 else if (elem_is_set (base, i))
813 elem_assign_contents (new_style, base, i);
814 else
815 continue;
816 elem_set (new_style, i);
817 elem_changed (new_style, i);
818 }
819 d(("copy merge %p\n", new_style));
820 return new_style;
821 }
822
823 /**
824 * gnm_style_ref: (skip)
825 * @style: #GnmStyle
826 *
827 * Returns: (transfer full): A new reference to @style.
828 */
829 GnmStyle *
gnm_style_ref(GnmStyle const * style)830 gnm_style_ref (GnmStyle const *style)
831 {
832 g_return_val_if_fail (style != NULL, NULL);
833 g_return_val_if_fail (style->ref_count > 0, NULL);
834
835 ((GnmStyle *)style)->ref_count++;
836 d(("ref %p = %d\n", style, style->ref_count));
837
838 return ((GnmStyle *)style);
839 }
840
841 /**
842 * gnm_style_unref: (skip)
843 * @style: #GnmStyle const
844 *
845 * Unrefs and _potentially frees_ @style.
846 * Takes a _const_ pointer to facilitate life cycles. The const indicates that
847 * the content cannot be changed, mainly when handling styles that are in the
848 * style hash.
849 **/
850 void
gnm_style_unref(GnmStyle const * style)851 gnm_style_unref (GnmStyle const *style)
852 {
853 g_return_if_fail (style != NULL);
854 g_return_if_fail (style->ref_count > 0);
855
856 d(("unref %p = %d\n", style, style->ref_count-1));
857 if (((GnmStyle *)style)->ref_count-- <= 1) {
858 GnmStyle *unconst = (GnmStyle *)style;
859 int i;
860
861 g_return_if_fail (style->link_count == 0);
862 g_return_if_fail (style->linked_sheet == NULL);
863
864 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
865 elem_clear_contents (unconst, i);
866 unconst->set = 0;
867 clear_conditional_merges (unconst);
868 gnm_style_clear_pango (unconst);
869 gnm_style_clear_font (unconst);
870
871 CHUNK_FREE (gnm_style_pool, unconst);
872 }
873 }
874
875 GType
gnm_style_get_type(void)876 gnm_style_get_type (void)
877 {
878 static GType t = 0;
879
880 if (t == 0) {
881 t = g_boxed_type_register_static ("GnmStyle",
882 (GBoxedCopyFunc)gnm_style_ref,
883 (GBoxedFreeFunc)gnm_style_unref);
884 }
885 return t;
886 }
887
888 /*
889 * Replace auto pattern color in style with sheet's auto pattern color.
890 * make_copy tells if we are allowed to modify the style in place or we must
891 * make a copy first.
892 */
893 static GnmStyle *
link_pattern_color(GnmStyle * style,GnmColor * auto_color,gboolean make_copy)894 link_pattern_color (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
895 {
896 GnmColor *pattern_color = style->color.pattern;
897
898 if (pattern_color->is_auto && auto_color != pattern_color) {
899 style_color_ref (auto_color);
900 if (make_copy) {
901 GnmStyle *orig = style;
902 style = gnm_style_dup (style);
903 gnm_style_unref (orig);
904 }
905 gnm_style_set_pattern_color (style, auto_color);
906 }
907 return style;
908 }
909
910 /*
911 * Replace auto border colors in style with sheet's auto pattern
912 * color. (pattern is *not* a typo.)
913 * make_copy tells if we are allowed to modify the style in place or we must
914 * make a copy first.
915 *
916 * FIXME: We conjecture that XL color 64 in border should change with the
917 * pattern, but not color 127. That distinction is not yet represented in
918 * our data structures.
919 */
920 static GnmStyle *
link_border_colors(GnmStyle * style,GnmColor * auto_color,gboolean make_copy)921 link_border_colors (GnmStyle *style, GnmColor *auto_color, gboolean make_copy)
922 {
923 int i;
924
925 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i) {
926 if (elem_is_set (style, i)) {
927 GnmBorder *border =
928 style->borders[i- MSTYLE_BORDER_TOP];
929 GnmColor *color;
930
931 if (!border)
932 continue;
933
934 color = border->color;
935 if (color->is_auto && auto_color != color) {
936 GnmBorder *new_border;
937 GnmStyleBorderOrientation orientation;
938
939 switch (i) {
940 case MSTYLE_BORDER_LEFT:
941 case MSTYLE_BORDER_RIGHT:
942 orientation = GNM_STYLE_BORDER_VERTICAL;
943 break;
944 case MSTYLE_BORDER_REV_DIAGONAL:
945 case MSTYLE_BORDER_DIAGONAL:
946 orientation = GNM_STYLE_BORDER_DIAGONAL;
947 break;
948 case MSTYLE_BORDER_TOP:
949 case MSTYLE_BORDER_BOTTOM:
950 default:
951 orientation = GNM_STYLE_BORDER_HORIZONTAL;
952 break;
953 }
954 style_color_ref (auto_color);
955 new_border = gnm_style_border_fetch (
956 border->line_type, auto_color,
957 orientation);
958
959 if (make_copy) {
960 GnmStyle *orig = style;
961 style = gnm_style_dup (style);
962 gnm_style_unref (orig);
963 make_copy = FALSE;
964 }
965 gnm_style_set_border (style, i, new_border);
966 }
967 }
968 }
969 return style;
970 }
971
972 static void
gnm_style_linked_sheet_changed(GnmStyle * style)973 gnm_style_linked_sheet_changed (GnmStyle *style)
974 {
975 Sheet *sheet = style->linked_sheet;
976
977 if (elem_is_set (style, MSTYLE_VALIDATION) &&
978 style->validation &&
979 gnm_validation_get_sheet (style->validation) != sheet) {
980 GnmValidation *new_v = gnm_validation_dup_to (style->validation, sheet);
981 gnm_style_set_validation (style, new_v);
982 }
983
984 if (elem_is_set (style, MSTYLE_HLINK) &&
985 style->hlink &&
986 gnm_hlink_get_sheet (style->hlink) != sheet) {
987 GnmHLink *new_l = gnm_hlink_dup_to (style->hlink, sheet);
988 gnm_style_set_hlink (style, new_l);
989 }
990
991 if (elem_is_set (style, MSTYLE_CONDITIONS) &&
992 style->conditions &&
993 gnm_style_conditions_get_sheet (style->conditions) != sheet) {
994 GnmStyleConditions *new_c, *new_sc;
995
996 sheet_conditions_share_conditions_remove (style->conditions);
997 new_c = gnm_style_conditions_dup_to (style->conditions, sheet);
998 new_sc = sheet_conditions_share_conditions_add (new_c);
999 if (new_sc) {
1000 g_object_unref (new_c);
1001 new_c = new_sc;
1002 }
1003 gnm_style_set_conditions (style, new_c);
1004 }
1005 }
1006
1007 /**
1008 * gnm_style_link_sheet:
1009 * @style: (transfer full):
1010 * @sheet:
1011 *
1012 * ABSORBS a reference to the style and sets the link count to 1.
1013 *
1014 * Where auto pattern color occurs in the style (it may for pattern and
1015 * borders), it is replaced with the sheet's auto pattern color. We make
1016 * sure that we do not modify the style which was passed in to us, but also
1017 * that we don't copy more than once. The final argument to the
1018 * link_xxxxx_color functions tell whether or not to copy.
1019 */
1020 GnmStyle *
gnm_style_link_sheet(GnmStyle * style,Sheet * sheet)1021 gnm_style_link_sheet (GnmStyle *style, Sheet *sheet)
1022 {
1023 GnmColor *auto_color;
1024 gboolean style_is_orig = TRUE;
1025
1026 if (style->linked_sheet != NULL) {
1027 GnmStyle *orig = style;
1028 style = gnm_style_dup (style);
1029 gnm_style_unref (orig);
1030 style_is_orig = FALSE;
1031
1032 /* safety test */
1033 g_return_val_if_fail (style->linked_sheet != sheet, style);
1034 }
1035
1036 g_return_val_if_fail (style->link_count == 0, style);
1037 g_return_val_if_fail (style->linked_sheet == NULL, style);
1038
1039 auto_color = sheet_style_get_auto_pattern_color (sheet);
1040 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1041 style = link_pattern_color (style, auto_color, style_is_orig);
1042 style = link_border_colors (style, auto_color, style_is_orig);
1043 style_color_unref (auto_color);
1044
1045 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions) {
1046 // We actually change the style here, but the resulting
1047 // ->conditions should be equivalent.
1048 GnmStyleConditions *sc_new = sheet_conditions_share_conditions_add (style->conditions);
1049 if (sc_new)
1050 gnm_style_set_conditions (style, g_object_ref (sc_new));
1051 }
1052
1053 style->linked_sheet = sheet;
1054 style->link_count = 1;
1055
1056 gnm_style_linked_sheet_changed (style);
1057
1058 d(("link sheet %p = 1\n", style));
1059 return style;
1060 }
1061
1062 void
gnm_style_link(GnmStyle * style)1063 gnm_style_link (GnmStyle *style)
1064 {
1065 g_return_if_fail (style->link_count > 0);
1066
1067 style->link_count++;
1068 d(("link %p = %d\n", style, style->link_count));
1069 }
1070
1071 void
gnm_style_unlink(GnmStyle * style)1072 gnm_style_unlink (GnmStyle *style)
1073 {
1074 g_return_if_fail (style->link_count > 0);
1075
1076 d(("unlink %p = %d\n", style, style->link_count-1));
1077 if (style->link_count-- == 1) {
1078 if (elem_is_set (style, MSTYLE_CONDITIONS) && style->conditions)
1079 sheet_conditions_share_conditions_remove (style->conditions);
1080 sheet_style_unlink (style->linked_sheet, style);
1081 style->linked_sheet = NULL;
1082 gnm_style_unref (style);
1083 }
1084 }
1085
1086 // Internal function for sheet-style.c use only
1087 void
gnm_style_abandon_link(GnmStyle * style)1088 gnm_style_abandon_link (GnmStyle *style)
1089 {
1090 style->link_count = 0;
1091 style->linked_sheet = NULL;
1092 }
1093
1094 gboolean
gnm_style_eq(GnmStyle const * a,GnmStyle const * b)1095 gnm_style_eq (GnmStyle const *a, GnmStyle const *b)
1096 {
1097 return a == b;
1098 }
1099
1100 gboolean
gnm_style_equal(GnmStyle const * a,GnmStyle const * b)1101 gnm_style_equal (GnmStyle const *a, GnmStyle const *b)
1102 {
1103 int i;
1104
1105 if (a == b)
1106 return TRUE;
1107 if (a->set != b->set || !gnm_style_equal_XL (a, b))
1108 return FALSE;
1109 UNROLLED_FOR (i = MSTYLE_VALIDATION, i < MSTYLE_ELEMENT_MAX, i++, {
1110 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1111 return FALSE;
1112 });
1113
1114 return TRUE;
1115 }
1116
1117 gboolean
gnm_style_equal_XL(GnmStyle const * a,GnmStyle const * b)1118 gnm_style_equal_XL (GnmStyle const *a, GnmStyle const *b)
1119 {
1120 int i;
1121
1122 g_return_val_if_fail (a != NULL, FALSE);
1123 g_return_val_if_fail (b != NULL, FALSE);
1124
1125 if (a == b)
1126 return TRUE;
1127
1128 if ((a->set ^ b->set) & ((1u << MSTYLE_VALIDATION) - 1))
1129 return FALSE;
1130
1131 UNROLLED_FOR (i = MSTYLE_COLOR_BACK, i < MSTYLE_VALIDATION, i++, {
1132 if (elem_is_set (a, i) && !ELEM_IS_EQ (a, b, i))
1133 return FALSE;
1134 });
1135 return TRUE;
1136 }
1137
1138 /**
1139 * gnm_style_equal_elem:
1140 * @a: first style
1141 * @b: second style
1142 * @e: style element
1143 *
1144 * Returns: %TRUE, if the two styles have the same contents for the
1145 * given element, either because neither have it set, or because both
1146 * have it set and to the same value.
1147 */
1148 gboolean
gnm_style_equal_elem(GnmStyle const * a,GnmStyle const * b,GnmStyleElement e)1149 gnm_style_equal_elem (GnmStyle const *a, GnmStyle const *b, GnmStyleElement e)
1150 {
1151 if (elem_is_set (a, e))
1152 return elem_is_set (b, e) && elem_is_eq (a, b, e);
1153 else
1154 return !elem_is_set (b, e);
1155 }
1156
1157
1158
1159 #define CMP_TRY_NUMBER_RAW(a_,b_) \
1160 do { \
1161 if ((a_) < (b_)) return -1; \
1162 if ((a_) > (b_)) return -1; \
1163 } while (0)
1164
1165 #define CMP_TRY_NUMBER(e_,f_) \
1166 do { \
1167 if (elem_is_set (a, (e_))) \
1168 CMP_TRY_NUMBER_RAW(a->f_, b->f_); \
1169 } while (0)
1170
1171 #define CMP_TRY_COLOR(e_,f_) \
1172 do { \
1173 if (elem_is_set (a, (e_))) { \
1174 CMP_TRY_NUMBER_RAW(a->f_->is_auto, b->f_->is_auto); \
1175 CMP_TRY_NUMBER_RAW(a->f_->go_color, b->f_->go_color); \
1176 } \
1177 } while (0)
1178
1179 /*
1180 * Ordering of GnmStyles. Apart from FIXMEs, this shouldn't change
1181 * from one run to the next.
1182 */
1183 int
gnm_style_cmp(GnmStyle const * a,GnmStyle const * b)1184 gnm_style_cmp (GnmStyle const *a, GnmStyle const *b)
1185 {
1186 GnmStyleElement e;
1187
1188 if (a == b)
1189 return 0;
1190
1191 /*
1192 * Very quick comparison based on what is set. This also allows
1193 * us to check on one elem_is_set below.
1194 */
1195 CMP_TRY_NUMBER_RAW (a->set, b->set);
1196
1197 CMP_TRY_COLOR (MSTYLE_FONT_COLOR, color.font);
1198 CMP_TRY_COLOR (MSTYLE_COLOR_BACK, color.back);
1199 CMP_TRY_COLOR (MSTYLE_COLOR_PATTERN, color.pattern);
1200 for (e = MSTYLE_BORDER_TOP; e <= MSTYLE_BORDER_DIAGONAL; e++) {
1201 GnmBorder const *ba, *bb;
1202 if (!elem_is_set (a, e))
1203 continue;
1204 ba = a->borders[e - MSTYLE_BORDER_TOP];
1205 bb = b->borders[e - MSTYLE_BORDER_TOP];
1206 if (ba == bb)
1207 continue; /* Handles both being NULL */
1208 CMP_TRY_NUMBER_RAW(!!ba, !!bb);
1209 CMP_TRY_NUMBER_RAW(ba->line_type, bb->line_type);
1210 CMP_TRY_NUMBER_RAW(ba->color->go_color, bb->color->go_color);
1211 CMP_TRY_NUMBER_RAW(ba->begin_margin, bb->begin_margin);
1212 CMP_TRY_NUMBER_RAW(ba->end_margin, bb->end_margin);
1213 CMP_TRY_NUMBER_RAW(ba->width, bb->width);
1214 }
1215 CMP_TRY_NUMBER (MSTYLE_PATTERN, pattern);
1216 if (elem_is_set (a, MSTYLE_FONT_NAME)) {
1217 /* Plain strcmp, not utf-8. We need to see diffs. */
1218 int tmp = strcmp (a->font_detail.name->str,
1219 b->font_detail.name->str);
1220 if (tmp)
1221 return tmp;
1222 }
1223 CMP_TRY_NUMBER (MSTYLE_FONT_BOLD, font_detail.bold);
1224 CMP_TRY_NUMBER (MSTYLE_FONT_ITALIC, font_detail.italic);
1225 CMP_TRY_NUMBER (MSTYLE_FONT_UNDERLINE, font_detail.underline);
1226 CMP_TRY_NUMBER (MSTYLE_FONT_STRIKETHROUGH, font_detail.strikethrough);
1227 CMP_TRY_NUMBER (MSTYLE_FONT_SCRIPT, font_detail.script);
1228 CMP_TRY_NUMBER (MSTYLE_FONT_SIZE, font_detail.size);
1229 if (elem_is_set (a, MSTYLE_FORMAT)) {
1230 /* Plain strcmp, not utf-8. We need to see diffs. */
1231 int tmp = strcmp (go_format_as_XL (a->format),
1232 go_format_as_XL (b->format));
1233 if (tmp)
1234 return tmp;
1235 }
1236 CMP_TRY_NUMBER (MSTYLE_ALIGN_H, h_align);
1237 CMP_TRY_NUMBER (MSTYLE_ALIGN_V, v_align);
1238 CMP_TRY_NUMBER (MSTYLE_INDENT, indent);
1239 CMP_TRY_NUMBER (MSTYLE_ROTATION, rotation);
1240 CMP_TRY_NUMBER (MSTYLE_TEXT_DIR, text_dir);
1241 CMP_TRY_NUMBER (MSTYLE_WRAP_TEXT, wrap_text);
1242 CMP_TRY_NUMBER (MSTYLE_SHRINK_TO_FIT, shrink_to_fit);
1243 CMP_TRY_NUMBER (MSTYLE_CONTENTS_LOCKED, contents_locked);
1244 CMP_TRY_NUMBER (MSTYLE_CONTENTS_HIDDEN, contents_hidden);
1245 /* FIXME: validation */
1246 /* FIXME: hlink */
1247 /* FIXME: input_msg */
1248 /* FIXME: conditions */
1249 /* FIXME: cond_styles */
1250
1251 /* Last resort: pointer comparison. */
1252 return a < b ? -1 : +1;
1253 }
1254
1255 #undef CMP_TRY_NUMBER_RAW
1256 #undef CMP_TRY_NUMBER
1257 #undef CMP_TRY_COLOR
1258
1259
1260 /**
1261 * gnm_style_equal_header:
1262 * @a: #GnmStyle
1263 * @b: #GnmStyle
1264 * @top: is this a header vertically or horizontally
1265 *
1266 * Check to see if @a is different enough from @b to make us think that @a is
1267 * from a header.
1268 **/
1269 gboolean
gnm_style_equal_header(GnmStyle const * a,GnmStyle const * b,gboolean top)1270 gnm_style_equal_header (GnmStyle const *a, GnmStyle const *b, gboolean top)
1271 {
1272 int i = top ? MSTYLE_BORDER_BOTTOM : MSTYLE_BORDER_RIGHT;
1273
1274 if (!elem_is_eq (a, b, i))
1275 return FALSE;
1276 for (i = MSTYLE_COLOR_BACK; i <= MSTYLE_COLOR_PATTERN ; i++)
1277 if (!elem_is_eq (a, b, i))
1278 return FALSE;
1279 for (i = MSTYLE_FONT_COLOR; i <= MSTYLE_SHRINK_TO_FIT ; i++)
1280 if (!elem_is_eq (a, b, i))
1281 return FALSE;
1282 return TRUE;
1283 }
1284
1285
1286 gboolean
gnm_style_is_element_set(GnmStyle const * style,GnmStyleElement elem)1287 gnm_style_is_element_set (GnmStyle const *style, GnmStyleElement elem)
1288 {
1289 g_return_val_if_fail (style != NULL, FALSE);
1290 g_return_val_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX, FALSE);
1291 return elem_is_set (style, elem);
1292 }
1293
1294 /**
1295 * gnm_style_is_complete:
1296 * @style: #GnmStyle to query
1297 *
1298 * Returns: %TRUE if all elements are set.
1299 **/
1300 gboolean
gnm_style_is_complete(GnmStyle const * style)1301 gnm_style_is_complete (GnmStyle const *style)
1302 {
1303 g_return_val_if_fail (style != NULL, FALSE);
1304
1305 return style->set == ((1u << MSTYLE_ELEMENT_MAX) - 1);
1306 }
1307
1308 void
gnm_style_unset_element(GnmStyle * style,GnmStyleElement elem)1309 gnm_style_unset_element (GnmStyle *style, GnmStyleElement elem)
1310 {
1311 g_return_if_fail (style != NULL);
1312 g_return_if_fail (MSTYLE_COLOR_BACK <= elem && elem < MSTYLE_ELEMENT_MAX);
1313
1314 if (elem_is_set (style, elem)) {
1315 elem_clear_contents (style, elem);
1316 elem_unset (style, elem);
1317 }
1318 }
1319
1320 /**
1321 * gnm_style_merge:
1322 * @base: #GnmStyle
1323 * @overlay: #GnmStyle
1324 *
1325 * Applies all active elements of @overlay onto @base.
1326 **/
1327 void
gnm_style_merge(GnmStyle * base,GnmStyle const * overlay)1328 gnm_style_merge (GnmStyle *base, GnmStyle const *overlay)
1329 {
1330 unsigned i;
1331 if (base == overlay)
1332 return;
1333 for (i = 0; i < MSTYLE_ELEMENT_MAX; i++)
1334 if (elem_is_set (overlay, i)) {
1335 elem_clear_contents (base, i);
1336 elem_assign_contents (base, overlay, i);
1337 elem_changed (base, i);
1338 }
1339 }
1340
1341 /**
1342 * gnm_style_merge_element:
1343 * @dst: Destination style
1344 * @src: Source style
1345 * @elem: Element to replace
1346 *
1347 * This function replaces element @elem in style @dst with element @elem
1348 * in style @src. (If element @elem was already set in style @dst then
1349 * the element will first be unset)
1350 **/
1351 void
gnm_style_merge_element(GnmStyle * dst,GnmStyle const * src,GnmStyleElement elem)1352 gnm_style_merge_element (GnmStyle *dst, GnmStyle const *src, GnmStyleElement elem)
1353 {
1354 g_return_if_fail (src != NULL);
1355 g_return_if_fail (dst != NULL);
1356 g_return_if_fail (src != dst);
1357
1358 if (elem_is_set (src, elem)) {
1359 elem_clear_contents (dst, elem);
1360 elem_assign_contents (dst, src, elem);
1361 elem_set (dst, elem);
1362 elem_changed (dst, elem);
1363 }
1364 }
1365
1366 /**
1367 * gnm_style_set_font_color:
1368 * @style: #GnmStyle to change
1369 * @col: (transfer full): #GnmColor
1370 *
1371 * Set the color used for fonts.
1372 */
1373 void
gnm_style_set_font_color(GnmStyle * style,GnmColor * col)1374 gnm_style_set_font_color (GnmStyle *style, GnmColor *col)
1375 {
1376 g_return_if_fail (style != NULL);
1377 g_return_if_fail (col != NULL);
1378
1379 elem_changed (style, MSTYLE_FONT_COLOR);
1380 if (elem_is_set (style, MSTYLE_FONT_COLOR))
1381 style_color_unref (style->color.font);
1382 else
1383 elem_set (style, MSTYLE_FONT_COLOR);
1384 elem_changed (style, MSTYLE_FONT_COLOR);
1385 style->color.font = col;
1386 gnm_style_clear_pango (style);
1387 }
1388
1389 /**
1390 * gnm_style_set_back_color:
1391 * @style: #GnmStyle to change
1392 * @col: (transfer full): #GnmColor
1393 *
1394 * Assigns @col as the background of @style.
1395 *
1396 * NOTE: the background colour is only visible if GnmStyle::pattern > 0
1397 **/
1398 void
gnm_style_set_back_color(GnmStyle * style,GnmColor * col)1399 gnm_style_set_back_color (GnmStyle *style, GnmColor *col)
1400 {
1401 g_return_if_fail (style != NULL);
1402 g_return_if_fail (col != NULL);
1403
1404 elem_changed (style, MSTYLE_COLOR_BACK);
1405 if (elem_is_set (style, MSTYLE_COLOR_BACK))
1406 style_color_unref (style->color.back);
1407 else
1408 elem_set (style, MSTYLE_COLOR_BACK);
1409 style->color.back = col;
1410 gnm_style_clear_pango (style);
1411 }
1412
1413 /**
1414 * gnm_style_set_pattern_color:
1415 * @style: #GnmStyle to change
1416 * @col: (transfer full): #GnmColor
1417 *
1418 * Set the color used for pattern.
1419 */
1420 void
gnm_style_set_pattern_color(GnmStyle * style,GnmColor * col)1421 gnm_style_set_pattern_color (GnmStyle *style, GnmColor *col)
1422 {
1423 g_return_if_fail (style != NULL);
1424 g_return_if_fail (col != NULL);
1425
1426 elem_changed (style, MSTYLE_COLOR_PATTERN);
1427 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
1428 style_color_unref (style->color.pattern);
1429 else
1430 elem_set (style, MSTYLE_COLOR_PATTERN);
1431 style->color.pattern = col;
1432 gnm_style_clear_pango (style);
1433 }
1434
1435 /**
1436 * gnm_style_get_font_color:
1437 * @style: #GnmStyle to query
1438 *
1439 * Returns: (transfer none) (nullable): #GnmColor used for font.
1440 */
1441 GnmColor *
gnm_style_get_font_color(GnmStyle const * style)1442 gnm_style_get_font_color (GnmStyle const *style)
1443 {
1444 g_return_val_if_fail (style != NULL, NULL);
1445 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_COLOR), NULL);
1446 return style->color.font;
1447 }
1448
1449 /**
1450 * gnm_style_get_back_color:
1451 * @style: #GnmStyle to query
1452 *
1453 * Returns: (transfer none) (nullable): #GnmColor used for background.
1454 */
1455 GnmColor *
gnm_style_get_back_color(GnmStyle const * style)1456 gnm_style_get_back_color (GnmStyle const *style)
1457 {
1458 g_return_val_if_fail (style != NULL, NULL);
1459 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_BACK), NULL);
1460 return style->color.back;
1461 }
1462
1463 /**
1464 * gnm_style_get_pattern_color:
1465 * @style: #GnmStyle to query
1466 *
1467 * Returns: (transfer none) (nullable): #GnmColor used for pattern.
1468 */
1469 GnmColor *
gnm_style_get_pattern_color(GnmStyle const * style)1470 gnm_style_get_pattern_color (GnmStyle const *style)
1471 {
1472 g_return_val_if_fail (style != NULL, NULL);
1473 g_return_val_if_fail (elem_is_set (style, MSTYLE_COLOR_PATTERN), NULL);
1474 return style->color.pattern;
1475 }
1476
1477 /**
1478 * gnm_style_set_border:
1479 * @style: #GnmStyle to change
1480 * @elem: Border element
1481 * @border: (transfer full) (nullable): new #GnmBorder for @style.
1482 */
1483 void
gnm_style_set_border(GnmStyle * style,GnmStyleElement elem,GnmBorder * border)1484 gnm_style_set_border (GnmStyle *style, GnmStyleElement elem,
1485 GnmBorder *border)
1486 {
1487 g_return_if_fail (style != NULL);
1488
1489 /* NOTE : It is legal for border to be NULL */
1490 switch (elem) {
1491 case MSTYLE_ANY_BORDER:
1492 elem_changed (style, elem);
1493 elem_set (style, elem);
1494 elem -= MSTYLE_BORDER_TOP;
1495 gnm_style_border_unref (style->borders[elem]);
1496 style->borders[elem] = border;
1497 break;
1498 default:
1499 g_warning ("Not a border element");
1500 break;
1501 }
1502 }
1503
1504 /**
1505 * gnm_style_get_border:
1506 * @style: #GnmStyle to query
1507 * @elem: Border element
1508 *
1509 * Returns: (transfer none) (nullable): The #GnmBorder for a single
1510 * border element.
1511 */
1512 GnmBorder *
gnm_style_get_border(GnmStyle const * style,GnmStyleElement elem)1513 gnm_style_get_border (GnmStyle const *style, GnmStyleElement elem)
1514 {
1515 g_return_val_if_fail (style != NULL, NULL);
1516
1517 switch (elem) {
1518 case MSTYLE_ANY_BORDER:
1519 return style->borders[elem - MSTYLE_BORDER_TOP ];
1520
1521 default:
1522 g_warning ("Not a border element");
1523 return NULL;
1524 }
1525 }
1526
1527 /**
1528 * gnm_style_set_pattern:
1529 * @style: #GnmStyle to change
1530 * @pattern: pattern code
1531 **/
1532 void
gnm_style_set_pattern(GnmStyle * style,int pattern)1533 gnm_style_set_pattern (GnmStyle *style, int pattern)
1534 {
1535 g_return_if_fail (style != NULL);
1536 g_return_if_fail (pattern >= 0);
1537 g_return_if_fail (pattern < GNM_PATTERNS_MAX);
1538
1539 elem_changed (style, MSTYLE_PATTERN);
1540 elem_set (style, MSTYLE_PATTERN);
1541 style->pattern = pattern;
1542 }
1543
1544 int
gnm_style_get_pattern(GnmStyle const * style)1545 gnm_style_get_pattern (GnmStyle const *style)
1546 {
1547 g_return_val_if_fail (style != NULL, 0);
1548 g_return_val_if_fail (elem_is_set (style, MSTYLE_PATTERN), 0);
1549
1550 return style->pattern;
1551 }
1552
1553 /**
1554 * gnm_style_get_font:
1555 * @style: #GnmStyle to query
1556 * @context: #PangoContext
1557 *
1558 * Returns: (transfer none): GnmFont implied by @style.
1559 **/
1560 GnmFont *
gnm_style_get_font(GnmStyle const * style,PangoContext * context)1561 gnm_style_get_font (GnmStyle const *style, PangoContext *context)
1562 {
1563 g_return_val_if_fail (style != NULL, NULL);
1564
1565 if (!style->font || style->font_context != context) {
1566 char const *name;
1567 gboolean bold, italic;
1568 double size;
1569
1570 gnm_style_clear_font ((GnmStyle *)style);
1571
1572 if (elem_is_set (style, MSTYLE_FONT_NAME))
1573 name = gnm_style_get_font_name (style);
1574 else
1575 name = DEFAULT_FONT;
1576
1577 if (elem_is_set (style, MSTYLE_FONT_BOLD))
1578 bold = gnm_style_get_font_bold (style);
1579 else
1580 bold = FALSE;
1581
1582 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
1583 italic = gnm_style_get_font_italic (style);
1584 else
1585 italic = FALSE;
1586
1587 if (elem_is_set (style, MSTYLE_FONT_SIZE))
1588 size = gnm_style_get_font_size (style);
1589 else
1590 size = DEFAULT_SIZE;
1591
1592 ((GnmStyle *)style)->font =
1593 gnm_font_new (context, name, size, bold, italic);
1594 ((GnmStyle *)style)->font_context = g_object_ref (context);
1595 }
1596
1597 return style->font;
1598 }
1599
1600 /**
1601 * gnm_style_set_font_name:
1602 * @style: #GnmStyle to change
1603 * @name: the font name as a string
1604 */
1605 void
gnm_style_set_font_name(GnmStyle * style,char const * name)1606 gnm_style_set_font_name (GnmStyle *style, char const *name)
1607 {
1608 g_return_if_fail (name != NULL);
1609 g_return_if_fail (style != NULL);
1610
1611 elem_changed (style, MSTYLE_FONT_NAME);
1612 if (elem_is_set (style, MSTYLE_FONT_NAME))
1613 go_string_unref (style->font_detail.name);
1614 else
1615 elem_set (style, MSTYLE_FONT_NAME);
1616 style->font_detail.name = go_string_new (name);
1617 gnm_style_clear_font (style);
1618 gnm_style_clear_pango (style);
1619 }
1620
1621 /**
1622 * gnm_style_get_font_name:
1623 * @style: the style to query
1624 *
1625 * Returns: (transfer none): the currently set font name
1626 */
1627 char const *
gnm_style_get_font_name(GnmStyle const * style)1628 gnm_style_get_font_name (GnmStyle const *style)
1629 {
1630 g_return_val_if_fail (style != NULL, NULL);
1631 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_NAME), NULL);
1632
1633 return style->font_detail.name->str;
1634 }
1635
1636 /**
1637 * gnm_style_set_font_bold:
1638 * @style: #GnmStyle to change
1639 * @bold: %TRUE for bold, %FALSE for regular
1640 */
1641 void
gnm_style_set_font_bold(GnmStyle * style,gboolean bold)1642 gnm_style_set_font_bold (GnmStyle *style, gboolean bold)
1643 {
1644 g_return_if_fail (style != NULL);
1645
1646 elem_changed (style, MSTYLE_FONT_BOLD);
1647 elem_set (style, MSTYLE_FONT_BOLD);
1648 style->font_detail.bold = !!bold;
1649 gnm_style_clear_font (style);
1650 gnm_style_clear_pango (style);
1651 }
1652
1653 /**
1654 * gnm_style_get_font_bold:
1655 * @style: #GnmStyle to query
1656 *
1657 * Returns: %TRUE if the style has a bold font.
1658 */
1659 gboolean
gnm_style_get_font_bold(GnmStyle const * style)1660 gnm_style_get_font_bold (GnmStyle const *style)
1661 {
1662 g_return_val_if_fail (style != NULL, FALSE);
1663 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_BOLD), FALSE);
1664
1665 return style->font_detail.bold;
1666 }
1667
1668 /**
1669 * gnm_style_set_font_italic:
1670 * @style: #GnmStyle to change
1671 * @italic: %TRUE for italic, %FALSE for regular
1672 */
1673 void
gnm_style_set_font_italic(GnmStyle * style,gboolean italic)1674 gnm_style_set_font_italic (GnmStyle *style, gboolean italic)
1675 {
1676 g_return_if_fail (style != NULL);
1677
1678 elem_changed (style, MSTYLE_FONT_ITALIC);
1679 elem_set (style, MSTYLE_FONT_ITALIC);
1680 style->font_detail.italic = !!italic;
1681 gnm_style_clear_font (style);
1682 gnm_style_clear_pango (style);
1683 }
1684
1685 /**
1686 * gnm_style_get_font_italic:
1687 * @style: #GnmStyle to query
1688 *
1689 * Returns: %TRUE if the style has an italic font.
1690 */
1691 gboolean
gnm_style_get_font_italic(GnmStyle const * style)1692 gnm_style_get_font_italic (GnmStyle const *style)
1693 {
1694 g_return_val_if_fail (style != NULL, FALSE);
1695 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_ITALIC), FALSE);
1696
1697 return style->font_detail.italic;
1698 }
1699
1700 /**
1701 * gnm_style_set_font_uline:
1702 * @style: #GnmStyle to change
1703 * @ul: #GnmUnderline specifying type of underlining
1704 **/
1705 void
gnm_style_set_font_uline(GnmStyle * style,GnmUnderline const underline)1706 gnm_style_set_font_uline (GnmStyle *style, GnmUnderline const underline)
1707 {
1708 g_return_if_fail (style != NULL);
1709 g_return_if_fail (underline >= UNDERLINE_NONE && underline <= UNDERLINE_DOUBLE_LOW);
1710
1711 elem_changed (style, MSTYLE_FONT_UNDERLINE);
1712 elem_set (style, MSTYLE_FONT_UNDERLINE);
1713 style->font_detail.underline = underline;
1714 gnm_style_clear_pango (style);
1715 }
1716
1717 /**
1718 * gnm_style_get_font_uline:
1719 * @style: #GnmStyle to query
1720 *
1721 * Returns: #GnmUnderline specifying type of underlining
1722 **/
1723 GnmUnderline
gnm_style_get_font_uline(GnmStyle const * style)1724 gnm_style_get_font_uline (GnmStyle const *style)
1725 {
1726 g_return_val_if_fail (style != NULL, UNDERLINE_NONE);
1727 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_UNDERLINE), UNDERLINE_NONE);
1728
1729 return style->font_detail.underline;
1730 }
1731
1732 /**
1733 * gnm_style_set_font_strike:
1734 * @style: #GnmStyle to change
1735 * @strike: %TRUE for strikethrough, %FALSE for regular
1736 */
1737 void
gnm_style_set_font_strike(GnmStyle * style,gboolean strikethrough)1738 gnm_style_set_font_strike (GnmStyle *style, gboolean strikethrough)
1739 {
1740 g_return_if_fail (style != NULL);
1741
1742 elem_changed (style, MSTYLE_FONT_STRIKETHROUGH);
1743 elem_set (style, MSTYLE_FONT_STRIKETHROUGH);
1744 style->font_detail.strikethrough = !!strikethrough;
1745 gnm_style_clear_pango (style);
1746 }
1747
1748 /**
1749 * gnm_style_get_font_strike:
1750 * @style: #GnmStyle to query
1751 *
1752 * Returns: %TRUE for strikethrough, %FALSE for regular
1753 */
1754 gboolean
gnm_style_get_font_strike(GnmStyle const * style)1755 gnm_style_get_font_strike (GnmStyle const *style)
1756 {
1757 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH), FALSE);
1758
1759 return style->font_detail.strikethrough;
1760 }
1761
1762 /**
1763 * gnm_style_set_font_script:
1764 * @style: #GnmStyle to change
1765 * @script: #GOFontScript specifying super or subscript
1766 **/
1767 void
gnm_style_set_font_script(GnmStyle * style,GOFontScript script)1768 gnm_style_set_font_script (GnmStyle *style, GOFontScript script)
1769 {
1770 g_return_if_fail (style != NULL);
1771 elem_changed (style, MSTYLE_FONT_SCRIPT);
1772 elem_set (style, MSTYLE_FONT_SCRIPT);
1773 style->font_detail.script = script;
1774 gnm_style_clear_pango (style);
1775 }
1776
1777 /**
1778 * gnm_style_get_font_script:
1779 * @style: #GnmStyle to query
1780 *
1781 * Returns: #GOFontScript specifying super or subscript
1782 **/
1783 GOFontScript
gnm_style_get_font_script(GnmStyle const * style)1784 gnm_style_get_font_script (GnmStyle const *style)
1785 {
1786 g_return_val_if_fail (style != NULL, GO_FONT_SCRIPT_STANDARD);
1787 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SCRIPT), GO_FONT_SCRIPT_STANDARD);
1788
1789 return style->font_detail.script;
1790 }
1791
1792 /**
1793 * gnm_style_set_font_size:
1794 * @style: #GnmStyle to change
1795 * @size: Font size in points
1796 **/
1797 void
gnm_style_set_font_size(GnmStyle * style,double size)1798 gnm_style_set_font_size (GnmStyle *style, double size)
1799 {
1800 g_return_if_fail (style != NULL);
1801 g_return_if_fail (size >= 1.);
1802 elem_changed (style, MSTYLE_FONT_SIZE);
1803 elem_set (style, MSTYLE_FONT_SIZE);
1804 style->font_detail.size = size;
1805 gnm_style_clear_font (style);
1806 gnm_style_clear_pango (style);
1807 }
1808
1809 /**
1810 * gnm_style_get_font_size:
1811 * @style: #GnmStyle to query
1812 *
1813 * Returns: Font size in points
1814 **/
1815 double
gnm_style_get_font_size(GnmStyle const * style)1816 gnm_style_get_font_size (GnmStyle const *style)
1817 {
1818 g_return_val_if_fail (style != NULL, 12.0);
1819 g_return_val_if_fail (elem_is_set (style, MSTYLE_FONT_SIZE), 12.0);
1820
1821 return style->font_detail.size;
1822 }
1823
1824 /**
1825 * gnm_style_set_format:
1826 * @style: #GnmStyle to change
1827 * @fmt: #GOFormat
1828 */
1829 void
gnm_style_set_format(GnmStyle * style,GOFormat const * fmt)1830 gnm_style_set_format (GnmStyle *style, GOFormat const *fmt)
1831 {
1832 g_return_if_fail (style != NULL);
1833 g_return_if_fail (fmt != NULL);
1834
1835 elem_changed (style, MSTYLE_FORMAT);
1836 go_format_ref (fmt);
1837 elem_clear_contents (style, MSTYLE_FORMAT);
1838 elem_set (style, MSTYLE_FORMAT);
1839 style->format = fmt;
1840 }
1841
1842 /*
1843 * gnm_style_set_format_text:
1844 * @style: mstyle to change.
1845 * @format: An *untranslated* format string.
1846 */
1847 void
gnm_style_set_format_text(GnmStyle * style,char const * format)1848 gnm_style_set_format_text (GnmStyle *style, char const *format)
1849 {
1850 GOFormat *sf;
1851
1852 g_return_if_fail (style != NULL);
1853 g_return_if_fail (format != NULL);
1854
1855 sf = go_format_new_from_XL (format);
1856 gnm_style_set_format (style, sf);
1857 go_format_unref (sf);
1858 }
1859
1860 /**
1861 * gnm_style_get_format:
1862 * @style: #GnmStyle to query
1863 *
1864 * Returns: (transfer none): #GOFormat
1865 */
1866 const GOFormat *
gnm_style_get_format(GnmStyle const * style)1867 gnm_style_get_format (GnmStyle const *style)
1868 {
1869 g_return_val_if_fail (style != NULL, NULL);
1870 g_return_val_if_fail (elem_is_set (style, MSTYLE_FORMAT), NULL);
1871
1872 return style->format;
1873 }
1874
1875 /**
1876 * gnm_style_set_align_h:
1877 * @style: #GnmStyle to change
1878 * @a: A #GnmHAlign
1879 **/
1880 void
gnm_style_set_align_h(GnmStyle * style,GnmHAlign a)1881 gnm_style_set_align_h (GnmStyle *style, GnmHAlign a)
1882 {
1883 g_return_if_fail (style != NULL);
1884
1885 elem_changed (style, MSTYLE_ALIGN_H);
1886 elem_set (style, MSTYLE_ALIGN_H);
1887 style->h_align = a;
1888 }
1889
1890 /**
1891 * gnm_style_get_align_h:
1892 * @style: #GnmStyle to query
1893 *
1894 * Returns: A #GnmHAlign
1895 **/
1896 GnmHAlign
gnm_style_get_align_h(GnmStyle const * style)1897 gnm_style_get_align_h (GnmStyle const *style)
1898 {
1899 g_return_val_if_fail (style != NULL, GNM_HALIGN_LEFT);
1900 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), GNM_HALIGN_LEFT);
1901
1902 return style->h_align;
1903 }
1904
1905 /**
1906 * gnm_style_set_align_v:
1907 * @style: #GnmStyle to change
1908 * @a: A #GnmVAlign
1909 **/
1910 void
gnm_style_set_align_v(GnmStyle * style,GnmVAlign a)1911 gnm_style_set_align_v (GnmStyle *style, GnmVAlign a)
1912 {
1913 g_return_if_fail (style != NULL);
1914
1915 elem_changed (style, MSTYLE_ALIGN_V);
1916 elem_set (style, MSTYLE_ALIGN_V);
1917 style->v_align = a;
1918 }
1919
1920 /**
1921 * gnm_style_get_align_v:
1922 * @style: #GnmStyle to query
1923 *
1924 * Returns: A #GnmVAlign
1925 **/
1926 GnmVAlign
gnm_style_get_align_v(GnmStyle const * style)1927 gnm_style_get_align_v (GnmStyle const *style)
1928 {
1929 g_return_val_if_fail (style != NULL, GNM_VALIGN_TOP);
1930 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), GNM_VALIGN_TOP);
1931
1932 return style->v_align;
1933 }
1934
1935 /**
1936 * gnm_style_set_indent:
1937 * @style: #GnmStyle to change
1938 * @i: Indentation amount
1939 **/
1940 void
gnm_style_set_indent(GnmStyle * style,int i)1941 gnm_style_set_indent (GnmStyle *style, int i)
1942 {
1943 g_return_if_fail (style != NULL);
1944
1945 elem_changed (style, MSTYLE_INDENT);
1946 elem_set (style, MSTYLE_INDENT);
1947 style->indent = i;
1948 }
1949
1950 /**
1951 * gnm_style_get_indent:
1952 * @style: #GnmStyle to query
1953 *
1954 * Returns: Indentation amount
1955 **/
1956 int
gnm_style_get_indent(GnmStyle const * style)1957 gnm_style_get_indent (GnmStyle const *style)
1958 {
1959 g_return_val_if_fail (style != NULL, 0);
1960 g_return_val_if_fail (elem_is_set (style, MSTYLE_INDENT), 0);
1961
1962 return style->indent;
1963 }
1964
1965 /**
1966 * gnm_style_set_rotation:
1967 * @style: #GnmStyle to change
1968 * @r: Rotation in degrees relative to horizontal
1969 **/
1970 void
gnm_style_set_rotation(GnmStyle * style,int rot_deg)1971 gnm_style_set_rotation (GnmStyle *style, int rot_deg)
1972 {
1973 g_return_if_fail (style != NULL);
1974
1975 elem_changed (style, MSTYLE_ROTATION);
1976 elem_set (style, MSTYLE_ROTATION);
1977 style->rotation = rot_deg;
1978 }
1979
1980 /**
1981 * gnm_style_get_rotation:
1982 * @style: #GnmStyle to query
1983 *
1984 * Returns: Rotation in degrees relative to horizontal
1985 **/
1986 int
gnm_style_get_rotation(GnmStyle const * style)1987 gnm_style_get_rotation (GnmStyle const *style)
1988 {
1989 g_return_val_if_fail (style != NULL, 0);
1990 g_return_val_if_fail (elem_is_set (style, MSTYLE_ROTATION), 0);
1991
1992 return style->rotation;
1993 }
1994
1995 /**
1996 * gnm_style_set_text_dir:
1997 * @style: #GnmStyle to change
1998 * @text_dir: A #GnmTextDir
1999 **/
2000 void
gnm_style_set_text_dir(GnmStyle * style,GnmTextDir text_dir)2001 gnm_style_set_text_dir (GnmStyle *style, GnmTextDir text_dir)
2002 {
2003 g_return_if_fail (style != NULL);
2004
2005 elem_changed (style, MSTYLE_TEXT_DIR);
2006 elem_set (style, MSTYLE_TEXT_DIR);
2007 style->text_dir = text_dir;
2008 }
2009
2010 /**
2011 * gnm_style_get_text_dir:
2012 * @style: #GnmStyle to query
2013 *
2014 * Returns: A #GnmTextDir
2015 **/
2016 GnmTextDir
gnm_style_get_text_dir(GnmStyle const * style)2017 gnm_style_get_text_dir (GnmStyle const *style)
2018 {
2019 g_return_val_if_fail (style != NULL, GNM_TEXT_DIR_CONTEXT);
2020 g_return_val_if_fail (elem_is_set (style, MSTYLE_TEXT_DIR), GNM_TEXT_DIR_CONTEXT);
2021
2022 return style->text_dir;
2023 }
2024
2025 /**
2026 * gnm_style_set_wrap_text:
2027 * @style: #GnmStyle to change
2028 * @f: %TRUE for wrapping, %FALSE for not
2029 **/
2030 void
gnm_style_set_wrap_text(GnmStyle * style,gboolean f)2031 gnm_style_set_wrap_text (GnmStyle *style, gboolean f)
2032 {
2033 g_return_if_fail (style != NULL);
2034
2035 elem_changed (style, MSTYLE_WRAP_TEXT);
2036 elem_set (style, MSTYLE_WRAP_TEXT);
2037 style->wrap_text = !!f;
2038 }
2039
2040 /**
2041 * gnm_style_get_wrap_text:
2042 * @style: #GnmStyle to query
2043 *
2044 * Returns: %TRUE for wrapping, %FALSE for not. See also
2045 * gnm_style_get_effective_wrap_text.
2046 **/
2047 gboolean
gnm_style_get_wrap_text(GnmStyle const * style)2048 gnm_style_get_wrap_text (GnmStyle const *style)
2049 {
2050 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
2051
2052 return style->wrap_text;
2053 }
2054
2055 /**
2056 * gnm_style_get_effective_wrap_text:
2057 * @style: #GnmStyle to query
2058 *
2059 * Returns: %TRUE for wrapping, %FALSE for not. This will be %TRUE also
2060 * when either alignment is JUSTIFY.
2061 **/
2062 gboolean
gnm_style_get_effective_wrap_text(GnmStyle const * style)2063 gnm_style_get_effective_wrap_text (GnmStyle const *style)
2064 {
2065 g_return_val_if_fail (style != NULL, FALSE);
2066 g_return_val_if_fail (elem_is_set (style, MSTYLE_WRAP_TEXT), FALSE);
2067 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_V), FALSE);
2068 g_return_val_if_fail (elem_is_set (style, MSTYLE_ALIGN_H), FALSE);
2069
2070 /* Note: GNM_HALIGN_GENERAL never expands to GNM_HALIGN_JUSTIFY. */
2071 return (style->wrap_text ||
2072 style->v_align == GNM_VALIGN_JUSTIFY ||
2073 style->v_align == GNM_VALIGN_DISTRIBUTED ||
2074 style->h_align == GNM_HALIGN_JUSTIFY);
2075 }
2076
2077 /**
2078 * gnm_style_set_shrink_to_fit:
2079 * @style: #GnmStyle to change
2080 * @f: %TRUE for shrink-to-fit, %FALSE for not
2081 **/
2082 void
gnm_style_set_shrink_to_fit(GnmStyle * style,gboolean f)2083 gnm_style_set_shrink_to_fit (GnmStyle *style, gboolean f)
2084 {
2085 g_return_if_fail (style != NULL);
2086
2087 elem_changed (style, MSTYLE_SHRINK_TO_FIT);
2088 elem_set (style, MSTYLE_SHRINK_TO_FIT);
2089 style->shrink_to_fit = !!f;
2090 }
2091
2092 /**
2093 * gnm_style_get_shrink_to_fit:
2094 * @style: #GnmStyle to query
2095 *
2096 * Returns: %TRUE for shrink-to-fit, %FALSE for not
2097 **/
2098 gboolean
gnm_style_get_shrink_to_fit(GnmStyle const * style)2099 gnm_style_get_shrink_to_fit (GnmStyle const *style)
2100 {
2101 g_return_val_if_fail (style != NULL, FALSE);
2102 g_return_val_if_fail (elem_is_set (style, MSTYLE_SHRINK_TO_FIT), FALSE);
2103
2104 return style->shrink_to_fit;
2105 }
2106
2107 /**
2108 * gnm_style_set_contents_locked:
2109 * @style: #GnmStyle to change
2110 * @f: %TRUE for locked, %FALSE for not
2111 **/
2112 void
gnm_style_set_contents_locked(GnmStyle * style,gboolean f)2113 gnm_style_set_contents_locked (GnmStyle *style, gboolean f)
2114 {
2115 g_return_if_fail (style != NULL);
2116
2117 elem_changed (style, MSTYLE_CONTENTS_LOCKED);
2118 elem_set (style, MSTYLE_CONTENTS_LOCKED);
2119 style->contents_locked = !!f;
2120 }
2121
2122 /**
2123 * gnm_style_get_contents_locked:
2124 * @style: #GnmStyle to query
2125 *
2126 * Returns: %TRUE for locked, %FALSE for not
2127 **/
2128 gboolean
gnm_style_get_contents_locked(GnmStyle const * style)2129 gnm_style_get_contents_locked (GnmStyle const *style)
2130 {
2131 g_return_val_if_fail (style != NULL, FALSE);
2132 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_LOCKED), FALSE);
2133
2134 return style->contents_locked;
2135 }
2136
2137 /**
2138 * gnm_style_set_contents_hidden:
2139 * @style: #GnmStyle to change
2140 * @f: %TRUE for hidden, %FALSE for not
2141 **/
2142 void
gnm_style_set_contents_hidden(GnmStyle * style,gboolean f)2143 gnm_style_set_contents_hidden (GnmStyle *style, gboolean f)
2144 {
2145 g_return_if_fail (style != NULL);
2146
2147 elem_changed (style, MSTYLE_CONTENTS_HIDDEN);
2148 elem_set (style, MSTYLE_CONTENTS_HIDDEN);
2149 style->contents_hidden = !!f;
2150 }
2151
2152 /**
2153 * gnm_style_get_contents_hidden:
2154 * @style: #GnmStyle to query
2155 *
2156 * Return: %TRUE for hidden, %FALSE for not
2157 **/
2158 gboolean
gnm_style_get_contents_hidden(GnmStyle const * style)2159 gnm_style_get_contents_hidden (GnmStyle const *style)
2160 {
2161 g_return_val_if_fail (style != NULL, FALSE);
2162 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN), FALSE);
2163
2164 return style->contents_hidden;
2165 }
2166
2167 /**
2168 * gnm_style_set_validation:
2169 * @style: #GnmStyle to change
2170 * @v: (transfer full) (nullable): #GnmValidation
2171 **/
2172 void
gnm_style_set_validation(GnmStyle * style,GnmValidation * v)2173 gnm_style_set_validation (GnmStyle *style, GnmValidation *v)
2174 {
2175 g_return_if_fail (style != NULL);
2176
2177 elem_clear_contents (style, MSTYLE_VALIDATION);
2178 elem_changed (style, MSTYLE_VALIDATION);
2179 elem_set (style, MSTYLE_VALIDATION);
2180 style->validation = v;
2181 }
2182
2183 /**
2184 * gnm_style_get_validation:
2185 * @style: #GnmStyle to query
2186 *
2187 * Returns: (transfer none) (nullable):
2188 **/
2189 GnmValidation const *
gnm_style_get_validation(GnmStyle const * style)2190 gnm_style_get_validation (GnmStyle const *style)
2191 {
2192 g_return_val_if_fail (style != NULL, NULL);
2193 g_return_val_if_fail (elem_is_set (style, MSTYLE_VALIDATION), NULL);
2194
2195 return style->validation;
2196 }
2197
2198 /**
2199 * gnm_style_set_hlink:
2200 * @style: #GnmStyle to change
2201 * @lnk: (transfer full) (nullable): #GnmHLink
2202 *
2203 * This sets a link for @style.
2204 **/
2205 void
gnm_style_set_hlink(GnmStyle * style,GnmHLink * lnk)2206 gnm_style_set_hlink (GnmStyle *style, GnmHLink *lnk)
2207 {
2208 g_return_if_fail (style != NULL);
2209
2210 elem_clear_contents (style, MSTYLE_HLINK);
2211 elem_changed (style, MSTYLE_HLINK);
2212 elem_set (style, MSTYLE_HLINK);
2213 style->hlink = lnk;
2214 }
2215
2216 /**
2217 * gnm_style_get_hlink:
2218 * @style: #GnmStyle to query
2219 *
2220 * Returns: (transfer none) (nullable): the associated #GnmHLink.
2221 **/
2222 GnmHLink *
gnm_style_get_hlink(GnmStyle const * style)2223 gnm_style_get_hlink (GnmStyle const *style)
2224 {
2225 g_return_val_if_fail (style != NULL, NULL);
2226 g_return_val_if_fail (elem_is_set (style, MSTYLE_HLINK), NULL);
2227
2228 return style->hlink;
2229 }
2230
2231 /**
2232 * gnm_style_set_input_msg:
2233 * @style: #GnmStyle to change
2234 * @msg: (transfer full) (nullable): #GnmInputMsg
2235 *
2236 * This sets an input message for @style.
2237 **/
2238 void
gnm_style_set_input_msg(GnmStyle * style,GnmInputMsg * msg)2239 gnm_style_set_input_msg (GnmStyle *style, GnmInputMsg *msg)
2240 {
2241 g_return_if_fail (style != NULL);
2242
2243 elem_clear_contents (style, MSTYLE_INPUT_MSG);
2244 elem_changed (style, MSTYLE_INPUT_MSG);
2245 elem_set (style, MSTYLE_INPUT_MSG);
2246 style->input_msg = msg;
2247 }
2248
2249 /**
2250 * gnm_style_get_input_msg:
2251 * @style: #GnmStyle to query
2252 *
2253 * Returns: (transfer none) (nullable): the currently set input message.
2254 **/
2255 GnmInputMsg *
gnm_style_get_input_msg(GnmStyle const * style)2256 gnm_style_get_input_msg (GnmStyle const *style)
2257 {
2258 g_return_val_if_fail (style != NULL, NULL);
2259 g_return_val_if_fail (elem_is_set (style, MSTYLE_INPUT_MSG), NULL);
2260
2261 return style->input_msg;
2262 }
2263
2264 /**
2265 * gnm_style_set_conditions:
2266 * @style: #GnmStyle to change
2267 * @sc: (transfer full) (nullable): #GnmStyleConditions
2268 *
2269 * This sets conditional style for @style.
2270 **/
2271 void
gnm_style_set_conditions(GnmStyle * style,GnmStyleConditions * sc)2272 gnm_style_set_conditions (GnmStyle *style, GnmStyleConditions *sc)
2273 {
2274 g_return_if_fail (style != NULL);
2275
2276 elem_clear_contents (style, MSTYLE_CONDITIONS);
2277 elem_changed (style, MSTYLE_CONDITIONS);
2278 elem_set (style, MSTYLE_CONDITIONS);
2279 style->conditions = sc;
2280 }
2281
2282 /**
2283 * gnm_style_get_conditions:
2284 * @style: #GnmStyle to query
2285 *
2286 * Returns: (transfer none) (nullable): the currently set conditional style.
2287 **/
2288 GnmStyleConditions *
gnm_style_get_conditions(GnmStyle const * style)2289 gnm_style_get_conditions (GnmStyle const *style)
2290 {
2291 g_return_val_if_fail (style != NULL, NULL);
2292 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2293 return style->conditions;
2294 }
2295
2296 /**
2297 * gnm_style_get_cond_style:
2298 * @style: #GnmStyle to query
2299 * @ix: The index of the condition for which style is desired
2300 *
2301 * Returns: (transfer none): the resulting style from applying the condition's
2302 * style overlay onto @style.
2303 **/
2304 GnmStyle const *
gnm_style_get_cond_style(GnmStyle const * style,int ix)2305 gnm_style_get_cond_style (GnmStyle const *style, int ix)
2306 {
2307 g_return_val_if_fail (style != NULL, NULL);
2308 g_return_val_if_fail (elem_is_set (style, MSTYLE_CONDITIONS), NULL);
2309 g_return_val_if_fail (style->conditions != NULL, NULL);
2310 g_return_val_if_fail (ix >= 0 && (unsigned)ix < gnm_style_conditions_details (style->conditions)->len, NULL);
2311
2312 if (style->changed)
2313 gnm_style_update ((GnmStyle *)style);
2314
2315 return g_ptr_array_index (style->cond_styles, ix);
2316 }
2317
2318 void
gnm_style_link_dependents(GnmStyle * style,GnmRange const * r)2319 gnm_style_link_dependents (GnmStyle *style, GnmRange const *r)
2320 {
2321 GnmStyleConditions *sc;
2322 Sheet *sheet;
2323
2324 g_return_if_fail (style != NULL);
2325 g_return_if_fail (r != NULL);
2326
2327 sheet = style->linked_sheet;
2328
2329 // ----------------------------------------
2330
2331 // Conditional formatting.
2332 //
2333 // We need to trigger a reformatting of the cell if a cell referenced
2334 // by the condition changes.
2335 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2336 ? gnm_style_get_conditions (style)
2337 : NULL;
2338 if (sc) {
2339 sheet_conditions_add (sheet, r, style);
2340 }
2341
2342 // ----------------------------------------
2343 // Validations.
2344 //
2345 // We can probably ignore those. If a dependent cell changes such
2346 // that a validation condition is no longer satisfied, it is
2347 // grandfathered in as valid.
2348 }
2349
2350 void
gnm_style_unlink_dependents(GnmStyle * style,GnmRange const * r)2351 gnm_style_unlink_dependents (GnmStyle *style, GnmRange const *r)
2352 {
2353 GnmStyleConditions *sc;
2354 Sheet *sheet;
2355
2356 g_return_if_fail (style != NULL);
2357 g_return_if_fail (r != NULL);
2358
2359 sheet = style->linked_sheet;
2360
2361 sc = elem_is_set (style, MSTYLE_CONDITIONS)
2362 ? gnm_style_get_conditions (style)
2363 : NULL;
2364 if (sc) {
2365 sheet_conditions_remove (sheet, r, style);
2366 }
2367
2368 // Validation -- see gnm_style_link_dependents
2369 }
2370
2371
2372 /**
2373 * gnm_style_visible_in_blank:
2374 * @style: style to query
2375 *
2376 * Returns: %TRUE if the style is visible, i.e., not transparent. Specifically
2377 * that means if it has a background or a visible border.
2378 */
2379 gboolean
gnm_style_visible_in_blank(GnmStyle const * style)2380 gnm_style_visible_in_blank (GnmStyle const *style)
2381 {
2382 GnmStyleElement i;
2383
2384 g_return_val_if_fail (style != NULL, FALSE);
2385
2386 if (elem_is_set (style, MSTYLE_PATTERN) &&
2387 gnm_style_get_pattern (style) > 0)
2388 return TRUE;
2389
2390 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2391 if (elem_is_set (style, i) &&
2392 gnm_style_border_visible_in_blank (gnm_style_get_border (style, i)))
2393 return TRUE;
2394
2395 return FALSE;
2396 }
2397
2398 static void
add_attr(PangoAttrList * attrs,PangoAttribute * attr)2399 add_attr (PangoAttrList *attrs, PangoAttribute *attr)
2400 {
2401 attr->start_index = 0;
2402 attr->end_index = G_MAXINT;
2403 pango_attr_list_insert (attrs, attr);
2404 }
2405
2406 /**
2407 * gnm_style_generate_attrs:
2408 * @style: style to query
2409 * @context: the context for the attributes
2410 * @zoom: zoom level
2411 *
2412 * Returns: (transfer full): a #PangoAttrList with attributes matching
2413 * @style. Attributes where the default will serve are not included.
2414 * The foreground color is not included.
2415 */
2416 PangoAttrList *
gnm_style_get_pango_attrs(GnmStyle const * style,PangoContext * context,double zoom)2417 gnm_style_get_pango_attrs (GnmStyle const *style,
2418 PangoContext *context,
2419 double zoom)
2420 {
2421 PangoAttrList *l;
2422 GnmUnderline ul;
2423 GnmFont *font = gnm_style_get_font (style, context);
2424
2425 if (style->pango_attrs) {
2426 if (zoom == style->pango_attrs_zoom) {
2427 pango_attr_list_ref (style->pango_attrs);
2428 return style->pango_attrs;
2429 }
2430 pango_attr_list_unref (((GnmStyle *)style)->pango_attrs);
2431 }
2432
2433 ((GnmStyle *)style)->pango_attrs = l = pango_attr_list_new ();
2434 ((GnmStyle *)style)->pango_attrs_zoom = zoom;
2435 ((GnmStyle *)style)->pango_attrs_height = -1;
2436
2437 /* Foreground colour. */
2438 /* See http://bugzilla.gnome.org/show_bug.cgi?id=105322 */
2439 if (0) {
2440 GnmColor const *fore = style->color.font;
2441 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2442 }
2443
2444 /* Handle underlining. */
2445 ul = gnm_style_get_font_uline (style);
2446 if (ul != UNDERLINE_NONE)
2447 add_attr (l,
2448 pango_attr_underline_new (gnm_translate_underline_to_pango (ul)));
2449
2450 /* Handle strikethrough. */
2451 if (gnm_style_get_font_strike (style))
2452 add_attr (l, pango_attr_strikethrough_new (TRUE));
2453
2454 /* Handle script. */
2455 switch (gnm_style_get_font_script (style)) {
2456 default:
2457 case GO_FONT_SCRIPT_STANDARD:
2458 break;
2459 case GO_FONT_SCRIPT_SUB:
2460 add_attr (l, go_pango_attr_subscript_new (TRUE));
2461 break;
2462 case GO_FONT_SCRIPT_SUPER:
2463 add_attr (l, go_pango_attr_superscript_new (TRUE));
2464 break;
2465 }
2466
2467 add_attr (l, pango_attr_font_desc_new (font->go.font->desc));
2468
2469 if (zoom != 1)
2470 add_attr (l, pango_attr_scale_new (zoom));
2471
2472 pango_attr_list_ref (l);
2473 return l;
2474 }
2475
2476 /**
2477 * gnm_style_generate_attrs_full:
2478 * @style: style to query
2479 *
2480 * Returns: (transfer full): a #PangoAttrList with attributes matching
2481 * @style, even attributes where the default would have served.
2482 */
2483 PangoAttrList *
gnm_style_generate_attrs_full(GnmStyle const * style)2484 gnm_style_generate_attrs_full (GnmStyle const *style)
2485 {
2486 GnmColor const *fore = style->color.font;
2487 PangoAttrList *l = pango_attr_list_new ();
2488
2489 add_attr (l, pango_attr_family_new (gnm_style_get_font_name (style)));
2490 add_attr (l, pango_attr_size_new (gnm_style_get_font_size (style) * PANGO_SCALE));
2491 add_attr (l, pango_attr_style_new (gnm_style_get_font_italic (style)
2492 ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL));
2493 add_attr (l, pango_attr_weight_new (gnm_style_get_font_bold (style)
2494 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL));
2495 add_attr (l, go_color_to_pango (fore->go_color, TRUE));
2496 add_attr (l, pango_attr_strikethrough_new
2497 (gnm_style_get_font_strike (style)));
2498 add_attr (l, pango_attr_underline_new
2499 (gnm_translate_underline_to_pango
2500 (gnm_style_get_font_uline (style))));
2501 return l;
2502 }
2503
2504 int
gnm_style_get_pango_height(GnmStyle const * style,PangoContext * context,double zoom)2505 gnm_style_get_pango_height (GnmStyle const *style,
2506 PangoContext *context,
2507 double zoom)
2508 {
2509 PangoAttrList *attrs = gnm_style_get_pango_attrs (style, context, zoom);
2510
2511 if (style->pango_attrs_height == -1) {
2512 int h;
2513 PangoLayout *layout = pango_layout_new (context);
2514 GOFormat const *fmt;
2515 gboolean requires_translation = FALSE;
2516
2517 fmt = gnm_style_get_format (style);
2518 if (!go_format_is_general (fmt)) {
2519 GOFormatDetails details;
2520 go_format_get_details (fmt, &details, NULL);
2521 if (details.family == GO_FORMAT_SCIENTIFIC &&
2522 details.use_markup) {
2523 PangoAttribute *a
2524 = go_pango_attr_superscript_new (TRUE);
2525 /* We want to superscript the "-01" in the */
2526 /* string "+1.23456789E-01" */
2527 a->start_index = 12;
2528 a->end_index = 15;
2529 pango_attr_list_insert (attrs, a);
2530 requires_translation = TRUE;
2531 }
2532 }
2533 pango_layout_set_attributes (layout, attrs);
2534 pango_layout_set_text (layout, "+1.23456789E-01", -1);
2535 if (requires_translation)
2536 go_pango_translate_layout (layout);
2537 pango_layout_get_pixel_size (layout, NULL, &h);
2538 g_object_unref (layout);
2539 ((GnmStyle *)style)->pango_attrs_height = h;
2540 }
2541
2542 pango_attr_list_unref (attrs);
2543 return style->pango_attrs_height;
2544 }
2545
2546
2547 void
gnm_style_set_from_pango_attribute(GnmStyle * style,PangoAttribute const * attr)2548 gnm_style_set_from_pango_attribute (GnmStyle *style, PangoAttribute const *attr)
2549 {
2550 switch (attr->klass->type) {
2551 case PANGO_ATTR_FAMILY:
2552 gnm_style_set_font_name (style, ((PangoAttrString *)attr)->value);
2553 break;
2554 case PANGO_ATTR_SIZE:
2555 gnm_style_set_font_size (style,
2556 ((PangoAttrInt *)attr)->value / (double)PANGO_SCALE);
2557 break;
2558 case PANGO_ATTR_STYLE:
2559 gnm_style_set_font_italic (style,
2560 ((PangoAttrInt *)attr)->value == PANGO_STYLE_ITALIC);
2561 break;
2562 case PANGO_ATTR_WEIGHT:
2563 gnm_style_set_font_bold (style,
2564 ((PangoAttrInt *)attr)->value >= PANGO_WEIGHT_BOLD);
2565 break;
2566 case PANGO_ATTR_FOREGROUND:
2567 gnm_style_set_font_color (style, gnm_color_new_pango (
2568 &((PangoAttrColor *)attr)->color));
2569 break;
2570 case PANGO_ATTR_UNDERLINE:
2571 gnm_style_set_font_uline
2572 (style, gnm_translate_underline_from_pango
2573 (((PangoAttrInt *)attr)->value));
2574 break;
2575 case PANGO_ATTR_STRIKETHROUGH:
2576 gnm_style_set_font_strike (style,
2577 ((PangoAttrInt *)attr)->value != 0);
2578 break;
2579 default : {
2580 gboolean script_seen = FALSE, script_set = FALSE;
2581 if (attr->klass->type == go_pango_attr_superscript_get_attr_type ()) {
2582 script_seen = TRUE;
2583 if (((GOPangoAttrSuperscript *)attr)->val == 1) {
2584 script_set = TRUE;
2585 gnm_style_set_font_script
2586 (style, GO_FONT_SCRIPT_SUPER);
2587 }
2588 } else if (attr->klass->type == go_pango_attr_subscript_get_attr_type ()) {
2589 script_seen = TRUE;
2590 if (((GOPangoAttrSubscript *)attr)->val == 1) {
2591 script_set = TRUE;
2592 gnm_style_set_font_script
2593 (style, GO_FONT_SCRIPT_SUB);
2594 }
2595 }
2596 if (script_seen && !script_set)
2597 gnm_style_set_font_script
2598 (style, GO_FONT_SCRIPT_STANDARD);
2599 break; /* ignored */
2600 }
2601 }
2602 }
2603
2604 /* ------------------------------------------------------------------------- */
2605
2606 static void
gnm_style_dump_color(GnmColor * color,GnmStyleElement elem)2607 gnm_style_dump_color (GnmColor *color, GnmStyleElement elem)
2608 {
2609 if (color)
2610 g_printerr ("\t%s: %x:%x:%x%s\n",
2611 gnm_style_element_name [elem],
2612 GO_COLOR_UINT_R (color->go_color),
2613 GO_COLOR_UINT_G (color->go_color),
2614 GO_COLOR_UINT_B (color->go_color),
2615 color->is_auto ? " auto" : "");
2616 else
2617 g_printerr ("\t%s: (NULL)\n", gnm_style_element_name [elem]);
2618 }
2619
2620 static void
gnm_style_dump_border(GnmBorder * border,GnmStyleElement elem)2621 gnm_style_dump_border (GnmBorder *border, GnmStyleElement elem)
2622 {
2623 g_printerr ("\t%s: ", gnm_style_element_name[elem]);
2624 if (border)
2625 g_printerr ("%d\n", border->line_type);
2626 else
2627 g_printerr ("blank\n");
2628 }
2629
2630 /**
2631 * gnm_style_dump:
2632 * @style: style to dump
2633 *
2634 * This function dumps the given style's contents to stderr. This is meant
2635 * for debug purposes only and doesn't do a very good job for, for example,
2636 * conditional style settings.
2637 */
2638 void
gnm_style_dump(GnmStyle const * style)2639 gnm_style_dump (GnmStyle const *style)
2640 {
2641 int i;
2642
2643 g_printerr ("Style Refs %d\n", style->ref_count);
2644 if (elem_is_set (style, MSTYLE_COLOR_BACK))
2645 gnm_style_dump_color (style->color.back, MSTYLE_COLOR_BACK);
2646 if (elem_is_set (style, MSTYLE_COLOR_PATTERN))
2647 gnm_style_dump_color (style->color.pattern, MSTYLE_COLOR_PATTERN);
2648
2649 for (i = MSTYLE_BORDER_TOP; i <= MSTYLE_BORDER_DIAGONAL; ++i)
2650 if (elem_is_set (style, i))
2651 gnm_style_dump_border (style->borders[i-MSTYLE_BORDER_TOP], i);
2652
2653 if (elem_is_set (style, MSTYLE_PATTERN))
2654 g_printerr ("\tpattern %d\n", style->pattern);
2655 if (elem_is_set (style, MSTYLE_FONT_COLOR))
2656 gnm_style_dump_color (style->color.font, MSTYLE_FONT_COLOR);
2657 if (elem_is_set (style, MSTYLE_FONT_NAME))
2658 g_printerr ("\tname '%s'\n", style->font_detail.name->str);
2659 if (elem_is_set (style, MSTYLE_FONT_BOLD))
2660 g_printerr (style->font_detail.bold ? "\tbold\n" : "\tnot bold\n");
2661 if (elem_is_set (style, MSTYLE_FONT_ITALIC))
2662 g_printerr (style->font_detail.italic ? "\titalic\n" : "\tnot italic\n");
2663 if (elem_is_set (style, MSTYLE_FONT_UNDERLINE))
2664 switch (style->font_detail.underline) {
2665 default:
2666 case UNDERLINE_NONE:
2667 g_printerr ("\tno underline\n"); break;
2668 case UNDERLINE_SINGLE:
2669 g_printerr ("\tsingle underline\n"); break;
2670 case UNDERLINE_DOUBLE:
2671 g_printerr ("\tdouble underline\n"); break;
2672 }
2673 if (elem_is_set (style, MSTYLE_FONT_STRIKETHROUGH))
2674 g_printerr (style->font_detail.strikethrough ? "\tstrikethrough\n" : "\tno strikethrough\n");
2675 if (elem_is_set (style, MSTYLE_FONT_SCRIPT))
2676 switch (style->font_detail.script) {
2677 case GO_FONT_SCRIPT_SUB:
2678 g_printerr ("\tsubscript\n"); break;
2679 default:
2680 case GO_FONT_SCRIPT_STANDARD:
2681 g_printerr ("\tno super or sub\n"); break;
2682 case GO_FONT_SCRIPT_SUPER:
2683 g_printerr ("\tsuperscript\n"); break;
2684 }
2685 if (elem_is_set (style, MSTYLE_FONT_SIZE))
2686 g_printerr ("\tsize %f\n", style->font_detail.size);
2687 if (elem_is_set (style, MSTYLE_FORMAT)) {
2688 const char *fmt = go_format_as_XL (style->format);
2689 g_printerr ("\tformat '%s'\n", fmt);
2690 }
2691 if (elem_is_set (style, MSTYLE_ALIGN_V))
2692 g_printerr ("\tvalign %hd\n", (short)style->v_align);
2693 if (elem_is_set (style, MSTYLE_ALIGN_H))
2694 g_printerr ("\thalign %hd\n", (short)style->h_align);
2695 if (elem_is_set (style, MSTYLE_INDENT))
2696 g_printerr ("\tindent %d\n", style->indent);
2697 if (elem_is_set (style, MSTYLE_ROTATION))
2698 g_printerr ("\trotation %d\n", style->rotation);
2699 if (elem_is_set (style, MSTYLE_TEXT_DIR))
2700 g_printerr ("\ttext dir %d\n", style->text_dir);
2701 if (elem_is_set (style, MSTYLE_WRAP_TEXT))
2702 g_printerr ("\twrap text %d\n", style->wrap_text);
2703 if (elem_is_set (style, MSTYLE_SHRINK_TO_FIT))
2704 g_printerr ("\tshrink to fit %d\n", style->shrink_to_fit);
2705 if (elem_is_set (style, MSTYLE_CONTENTS_LOCKED))
2706 g_printerr ("\tlocked %d\n", style->contents_locked);
2707 if (elem_is_set (style, MSTYLE_CONTENTS_HIDDEN))
2708 g_printerr ("\thidden %d\n", style->contents_hidden);
2709 if (elem_is_set (style, MSTYLE_VALIDATION))
2710 g_printerr ("\tvalidation %p\n", (void *)style->validation);
2711 if (elem_is_set (style, MSTYLE_HLINK))
2712 g_printerr ("\thlink %p\n", (void *)style->hlink);
2713 if (elem_is_set (style, MSTYLE_INPUT_MSG))
2714 g_printerr ("\tinput msg %p\n", (void *)style->input_msg);
2715 if (elem_is_set (style, MSTYLE_CONDITIONS))
2716 g_printerr ("\tconditions %p\n", (void *)style->conditions);
2717 }
2718
2719 /* ------------------------------------------------------------------------- */
2720
2721 /**
2722 * gnm_style_init: (skip)
2723 */
2724 void
gnm_style_init(void)2725 gnm_style_init (void)
2726 {
2727 debug_style_deps = gnm_debug_flag ("style-deps");
2728
2729 #if USE_MSTYLE_POOL
2730 gnm_style_pool =
2731 go_mem_chunk_new ("style pool",
2732 sizeof (GnmStyle),
2733 16 * 1024 - 128);
2734 #endif
2735 }
2736
2737 #if USE_MSTYLE_POOL
2738 static void
cb_gnm_style_pool_leak(gpointer data,G_GNUC_UNUSED gpointer user)2739 cb_gnm_style_pool_leak (gpointer data, G_GNUC_UNUSED gpointer user)
2740 {
2741 GnmStyle *style = data;
2742 g_printerr ("Leaking style at %p.\n", (void *)style);
2743 gnm_style_dump (style);
2744 }
2745 #endif
2746
2747 /**
2748 * gnm_style_shutdown: (skip)
2749 */
2750 void
gnm_style_shutdown(void)2751 gnm_style_shutdown (void)
2752 {
2753 #if USE_MSTYLE_POOL
2754 go_mem_chunk_foreach_leak (gnm_style_pool, cb_gnm_style_pool_leak, NULL);
2755 go_mem_chunk_destroy (gnm_style_pool, FALSE);
2756 gnm_style_pool = NULL;
2757 #endif
2758 }
2759