Update Spanish translation
[gnumeric.git] / src / style.c
bloba0ff5742e42088d3a9b3676adec11c94e22850bb
1 /*
2 * Style.c: Style resource management
4 * Author:
5 * Miguel de Icaza (miguel@gnu.org)
6 * (C) 1998-2004 Miguel de Icaza
7 */
8 #include <gnumeric-config.h>
9 #include <glib/gi18n-lib.h>
10 #include <gnumeric.h>
11 #include <style.h>
12 #include <style-font.h>
14 #include <gnm-format.h>
15 #include <style-color.h>
16 #include <application.h>
17 #include <sheet.h>
18 #include <cell.h>
19 #include <value.h>
21 #include <gui-util.h>
22 #include <mathfunc.h>
23 #include <gnumeric-conf.h>
25 #include <pango/pangocairo.h>
26 #include <gdk/gdk.h>
27 #include <string.h>
28 #include <goffice/goffice.h>
30 #undef DEBUG_REF_COUNT
31 #undef DEBUG_FONTS
33 static GHashTable *style_font_hash;
34 static GHashTable *style_font_negative_hash;
36 double gnm_font_default_width;
37 static char *gnumeric_default_font_name;
38 static double gnumeric_default_font_size;
40 /* This is very ad hoc - throw it away when something better comes along */
41 /* See also wine/dlls/winex11.drv/xfont.c */
43 static struct FontInfo {
44 const char *font_name;
45 const char *font_substitute_name;
46 int override_codepage;
47 } font_info[] = {
48 { "Times New Roman", "Times", -1 },
49 { "Times New Roman CYR", "Times", 1251 },
50 { "Times New Roman Greek", "Times", 1253 },
51 { "Times New Roman Tur", "Times", 1254 },
52 { "Times New Roman Baltic", "Times", 1257 },
53 { "Tms Rmn", "Times", -1 },
54 { "Arial", "Sans", -1 },
55 { "Arial CYR", "Sans", 1251 },
56 { "Arial Greek", "Sans", 1253 },
57 { "Arial Tur", "Sans", 1254 },
58 { "Arial Baltic", "Sans", 1257 },
59 { "Albany", "Sans", -1 },
60 { "Helvetica", "Sans", -1 },
61 { "Courier New", "Courier", -1 },
62 { "Courier New CYR", "Courier", 1251 },
63 { "Courier New Greek", "Courier", 1253 },
64 { "Courier New Tur", "Courier", 1254 },
65 { "Courier New Baltic", "Courier", 1257 },
66 { "£Í£Ó £Ð¥´¥·¥Ã¥¯", "Kochi Gothic", -1 },
67 { "£Í£Ó ¥´¥·¥Ã¥¯", "Kochi Gothic", -1 },
68 { "¥´¥·¥Ã¥¯", "Kochi Gothic", -1 },
69 { "MS UI Gothic", "Kochi Gothic", -1 },
70 { "£Í£Ó £ÐÌÀÄ«", "Kochi Mincho", -1 },
71 { "£Í£Ó ÌÀÄ«", "Kochi Mincho", -1 },
72 { "ÌÀÄ«", "Kochi Mincho", -1 },
73 { "GulimChe", NULL, 949 }
76 static struct FontInfo *
77 find_font (const char *font_name)
79 unsigned ui;
81 if (!font_name)
82 return NULL;
84 for (ui = 0; ui < G_N_ELEMENTS (font_info); ui++) {
85 if (!g_ascii_strcasecmp (font_info[ui].font_name, font_name))
86 return font_info + ui;
88 return NULL;
91 /**
92 * gnm_font_override_codepage:
93 * @font_name: The win32 font name
95 * Returns a codepage for the named Win32 font, or -1 if no such codepage
96 * is known.
98 int
99 gnm_font_override_codepage (gchar const *font_name)
101 struct FontInfo *fi = find_font (font_name);
102 return fi ? fi->override_codepage : -1;
107 * get_substitute_font:
108 * @font_name The font name
110 * Tries to find a gnome font which matches the Excel font.
111 * Returns the name of the substitute font if found. Otherwise returns NULL
113 static gchar const *
114 get_substitute_font (gchar const *font_name)
116 struct FontInfo *fi = find_font (font_name);
117 return fi ? fi->font_substitute_name : NULL;
120 static GnmFont *
121 style_font_new_simple (PangoContext *context,
122 char const *font_name, double size_pts,
123 gboolean bold, gboolean italic)
125 GnmFont *font;
126 GnmFont key;
128 if (font_name == NULL) {
129 g_warning ("font_name == NULL, using %s", DEFAULT_FONT);
130 font_name = DEFAULT_FONT;
132 if (size_pts <= 0) {
133 g_warning ("font_size <= 0, using %f", DEFAULT_SIZE);
134 size_pts = DEFAULT_SIZE;
137 /* This cast does not mean we will change the name. */
138 key.font_name = (char *)font_name;
139 key.size_pts = size_pts;
140 key.is_bold = bold;
141 key.is_italic = italic;
142 key.context = context;
144 font = (GnmFont *) g_hash_table_lookup (style_font_hash, &key);
145 if (font == NULL) {
146 PangoFontDescription *desc;
147 PangoFont *pango_font;
149 if (g_hash_table_lookup (style_font_negative_hash, &key))
150 return NULL;
152 font = g_new0 (GnmFont, 1);
153 font->font_name = g_strdup (font_name);
154 font->size_pts = size_pts;
155 font->is_bold = bold;
156 font->is_italic = italic;
157 font->context = g_object_ref (context);
158 /* One reference for the cache, one for the caller. */
159 font->ref_count = 2;
161 desc = pango_font_description_new ();
163 pango_font_description_set_family (desc, font_name);
164 pango_font_description_set_weight (desc,
165 bold ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL);
166 pango_font_description_set_style (desc,
167 italic ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL);
168 pango_font_description_set_size (desc, size_pts * PANGO_SCALE);
170 pango_font = pango_context_load_font (context, desc);
171 if (pango_font == NULL) {
172 /* if we fail, try to be smart and map to something similar */
173 char const *sub = get_substitute_font (font_name);
174 if (sub != NULL) {
175 pango_font_description_set_family (desc, font_name);
176 pango_font = pango_context_load_font (context,
177 desc);
180 if (pango_font == NULL) {
181 pango_font_description_free (desc);
182 g_hash_table_insert (style_font_negative_hash,
183 font, font);
184 return NULL;
188 if (pango_font)
189 g_object_unref (pango_font);
191 font->go.font = go_font_new_by_desc (desc);
192 font->go.metrics = go_font_metrics_new (context, font->go.font);
193 g_hash_table_insert (style_font_hash, font, font);
194 } else
195 font->ref_count++;
197 #ifdef DEBUG_REF_COUNT
198 g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
199 font, font->font_name,
200 font->is_bold ? " bold" : "",
201 font->is_italic ? " italic" : "",
202 font->ref_count);
203 #endif
204 return font;
207 GnmFont *
208 gnm_font_new (PangoContext *context,
209 char const *font_name, double size_pts,
210 gboolean bold, gboolean italic)
212 GnmFont *font;
214 g_return_val_if_fail (font_name != NULL, NULL);
215 g_return_val_if_fail (size_pts > 0, NULL);
217 font = style_font_new_simple (context, font_name, size_pts,
218 bold, italic);
219 if (font) return font;
221 font_name = gnumeric_default_font_name;
222 font = style_font_new_simple (context, font_name, size_pts,
223 bold, italic);
224 if (font) return font;
226 size_pts = gnumeric_default_font_size;
227 font = style_font_new_simple (context, font_name, size_pts,
228 bold, italic);
229 if (font) return font;
231 bold = FALSE;
232 font = style_font_new_simple (context, font_name, size_pts,
233 bold, italic);
234 if (font) return font;
236 italic = FALSE;
237 font = style_font_new_simple (context, font_name, size_pts,
238 bold, italic);
239 if (font) return font;
242 * This should not be possible to reach as we have reverted all the way
243 * back to the default font.
245 g_assert_not_reached ();
246 abort ();
249 GnmFont *
250 gnm_font_ref (GnmFont *sf)
252 g_return_val_if_fail (sf != NULL, NULL);
254 sf->ref_count++;
255 #ifdef DEBUG_REF_COUNT
256 g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
257 sf, sf->font_name,
258 sf->is_bold ? " bold" : "",
259 sf->is_italic ? " italic" : "",
260 sf->ref_count);
261 #endif
263 return sf;
266 void
267 gnm_font_unref (GnmFont *sf)
269 g_return_if_fail (sf != NULL);
270 g_return_if_fail (sf->ref_count > 0);
272 sf->ref_count--;
273 #ifdef DEBUG_REF_COUNT
274 g_message (__FUNCTION__ " font=%p name=%s%s%s ref_count=%d\n",
275 sf, sf->font_name,
276 sf->is_bold ? " bold" : "",
277 sf->is_italic ? " italic" : "",
278 sf->ref_count);
279 #endif
280 if (sf->ref_count != 0)
281 return;
283 g_hash_table_remove (style_font_hash, sf);
284 /* hash-changing operations after above line. */
286 if (sf->go.font) {
287 go_font_unref (sf->go.font);
288 sf->go.font = NULL;
291 if (sf->go.metrics) {
292 go_font_metrics_free (sf->go.metrics);
293 sf->go.metrics = NULL;
296 g_object_unref (sf->context);
297 sf->context = NULL;
299 g_free (sf->font_name);
300 sf->font_name = NULL;
302 g_free (sf);
305 GType
306 gnm_font_get_type (void)
308 static GType t = 0;
310 if (t == 0) {
311 t = g_boxed_type_register_static ("GnmFont",
312 (GBoxedCopyFunc)gnm_font_ref,
313 (GBoxedFreeFunc)gnm_font_unref);
315 return t;
318 gint
319 gnm_font_equal (gconstpointer v, gconstpointer v2)
321 GnmFont const *k1 = (GnmFont const *) v;
322 GnmFont const *k2 = (GnmFont const *) v2;
324 return (k1->size_pts == k2->size_pts &&
325 k1->is_bold == k2->is_bold &&
326 k1->is_italic == k2->is_italic &&
327 k1->context == k2->context &&
328 strcmp (k1->font_name, k2->font_name) == 0);
331 guint
332 gnm_font_hash (gconstpointer v)
334 GnmFont const *k = (GnmFont const *) v;
335 return (guint)k->size_pts ^
336 g_str_hash (k->font_name) ^
337 (k->is_bold ? 0x33333333 : 0) ^
338 (k->is_italic ? 0xcccccccc : 0) ^
339 GPOINTER_TO_UINT (k->context);
342 GType
343 gnm_align_h_get_type (void)
345 static GType etype = 0;
346 if (etype == 0) {
347 static GEnumValue const values[] = {
348 {GNM_HALIGN_GENERAL, "GNM_HALIGN_GENERAL", "general"},
349 {GNM_HALIGN_LEFT, "GNM_HALIGN_LEFT", "left"},
350 {GNM_HALIGN_RIGHT, "GNM_HALIGN_RIGHT", "right"},
351 {GNM_HALIGN_CENTER, "GNM_HALIGN_CENTER", "center"},
352 {GNM_HALIGN_FILL, "GNM_HALIGN_FILL", "fill"},
353 {GNM_HALIGN_JUSTIFY, "GNM_HALIGN_JUSTIFY", "justify"},
354 {GNM_HALIGN_CENTER_ACROSS_SELECTION,
355 "GNM_HALIGN_CENTER_ACROSS_SELECTION",
356 "across-selection"},
357 {GNM_HALIGN_DISTRIBUTED,
358 "GNM_HALIGN_DISTRIBUTED", "distributed"},
359 { 0, NULL, NULL }
361 etype = g_enum_register_static ("GnmHAlign",
362 values);
364 return etype;
367 GType
368 gnm_align_v_get_type (void)
370 static GType etype = 0;
371 if (etype == 0) {
372 static GEnumValue const values[] = {
373 {GNM_VALIGN_TOP, "GNM_VALIGN_TOP", "top"},
374 {GNM_VALIGN_BOTTOM, "GNM_VALIGN_BOTTOM", "bottom"},
375 {GNM_VALIGN_CENTER, "GNM_VALIGN_CENTER", "center"},
376 {GNM_VALIGN_JUSTIFY, "GNM_VALIGN_JUSTIFY", "justify"},
377 {GNM_VALIGN_DISTRIBUTED,
378 "GNM_VALIGN_DISTRIBUTED", "distributed"},
379 { 0, NULL, NULL }
381 etype = g_enum_register_static ("GnmVAlign",
382 values);
384 return etype;
389 static PangoFontMap *fontmap;
390 static PangoContext *context;
393 * gnm_pango_context_get:
395 * Simple wrapper to handle windowless operation
396 * Returns: (transfer full):
398 PangoContext *
399 gnm_pango_context_get (void)
401 if (!context) {
402 GdkScreen *screen = gdk_screen_get_default ();
404 if (screen != NULL) {
405 context = gdk_pango_context_get_for_screen (screen);
406 } else {
407 if (!fontmap)
408 fontmap = pango_cairo_font_map_new ();
409 pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (fontmap), 96);
410 context = pango_font_map_create_context (PANGO_FONT_MAP (fontmap));
412 pango_context_set_language (context, gtk_get_default_language ());
413 pango_context_set_base_dir (context, PANGO_DIRECTION_LTR);
416 return g_object_ref (context);
420 * gnm_font_init: (skip)
422 void
423 gnm_font_init (void)
425 PangoContext *context;
426 GnmFont *gnumeric_default_font = NULL;
427 double pts_scale = 72. / gnm_app_display_dpi_get (TRUE);
429 style_font_hash = g_hash_table_new (
430 gnm_font_hash, gnm_font_equal);
431 style_font_negative_hash = g_hash_table_new (
432 gnm_font_hash, gnm_font_equal);
434 gnumeric_default_font_name = g_strdup (gnm_conf_get_core_defaultfont_name ());
435 gnumeric_default_font_size = gnm_conf_get_core_defaultfont_size ();
437 context = gnm_pango_context_get ();
438 if (gnumeric_default_font_name && gnumeric_default_font_size >= 1)
439 gnumeric_default_font = style_font_new_simple (context,
440 gnumeric_default_font_name, gnumeric_default_font_size,
441 FALSE, FALSE);
442 if (gnumeric_default_font == NULL) {
443 g_warning ("Configured default font '%s %f' not available, trying fallback...",
444 gnumeric_default_font_name, gnumeric_default_font_size);
445 gnumeric_default_font = style_font_new_simple (context,
446 DEFAULT_FONT, DEFAULT_SIZE, FALSE, FALSE);
447 if (gnumeric_default_font != NULL) {
448 g_free (gnumeric_default_font_name);
449 gnumeric_default_font_name = g_strdup (DEFAULT_FONT);
450 gnumeric_default_font_size = DEFAULT_SIZE;
451 } else {
452 g_warning ("Fallback font '%s %f' not available, trying 'fixed'...",
453 DEFAULT_FONT, DEFAULT_SIZE);
454 gnumeric_default_font = style_font_new_simple (context,
455 "fixed", 10, FALSE, FALSE);
456 if (gnumeric_default_font != NULL) {
457 g_free (gnumeric_default_font_name);
458 gnumeric_default_font_name = g_strdup ("fixed");
459 gnumeric_default_font_size = 10;
460 } else {
461 g_warning ("Even 'fixed 10' failed ?? We're going to exit now,"
462 "there is something wrong with your font configuration");
463 exit (1);
468 gnm_font_default_width = pts_scale *
469 PANGO_PIXELS (gnumeric_default_font->go.metrics->avg_digit_width);
470 gnm_font_unref (gnumeric_default_font);
471 g_object_unref (context);
475 * gnm_font_shutdown: (skip)
477 * Release all resources allocated by gnm_font_init.
479 void
480 gnm_font_shutdown (void)
482 GList *fonts = NULL, *tmp;
484 g_free (gnumeric_default_font_name);
485 gnumeric_default_font_name = NULL;
487 // Make a list of the fonts, then unref them.
488 fonts = g_hash_table_get_keys (style_font_hash);
489 for (tmp = fonts; tmp; tmp = tmp->next) {
490 GnmFont *sf = tmp->data;
491 if (sf->ref_count != 1)
492 g_warning ("Font %s has %d references instead of the expected single.",
493 sf->font_name, sf->ref_count);
494 gnm_font_unref (sf);
496 g_list_free (fonts);
497 g_hash_table_destroy (style_font_hash);
498 style_font_hash = NULL;
500 fonts = g_hash_table_get_keys (style_font_negative_hash);
501 for (tmp = fonts; tmp; tmp = tmp->next) {
502 GnmFont *sf = tmp->data;
503 g_object_unref (sf->context);
504 g_free (sf->font_name);
505 g_free (sf);
507 g_list_free (fonts);
508 g_hash_table_destroy (style_font_negative_hash);
509 style_font_negative_hash = NULL;
511 if (context) {
512 g_object_unref (context);
513 context = NULL;
516 if (fontmap) {
517 /* Do this late -- see bugs 558100 and 558254. */
518 /* and not at all on win32, where the life cycle is different */
519 #ifndef G_OS_WIN32
520 g_object_unref (fontmap);
521 #endif
522 fontmap = NULL;
528 * gnm_style_required_spanflags:
529 * @style: the style
531 * What changes are required after applying the supplied style.
533 GnmSpanCalcFlags
534 gnm_style_required_spanflags (GnmStyle const *style)
536 GnmSpanCalcFlags res = GNM_SPANCALC_SIMPLE;
538 if (gnm_style_is_element_set (style, MSTYLE_CONDITIONS))
539 /* Note that style->cond_styles may not be set yet */
540 /* More importantly, even if the conditions are empty we */
541 /* have to rerender everything since we do not know what changed. */
542 res |= GNM_SPANCALC_RE_RENDER | GNM_SPANCALC_RESIZE | GNM_SPANCALC_ROW_HEIGHT;
543 else {
544 gboolean const row_height =
545 gnm_style_is_element_set (style, MSTYLE_FONT_SIZE) ||
546 gnm_style_is_element_set (style, MSTYLE_WRAP_TEXT) ||
547 gnm_style_is_element_set (style, MSTYLE_ROTATION) ||
548 gnm_style_is_element_set (style, MSTYLE_FONT_SCRIPT);
549 gboolean const size_change = row_height ||
550 gnm_style_is_element_set (style, MSTYLE_FONT_NAME) ||
551 gnm_style_is_element_set (style, MSTYLE_FONT_BOLD) ||
552 gnm_style_is_element_set (style, MSTYLE_FONT_ITALIC);
553 gboolean const format_change =
554 gnm_style_is_element_set (style, MSTYLE_FORMAT) ||
555 gnm_style_is_element_set (style, MSTYLE_INDENT) ||
556 gnm_style_is_element_set (style, MSTYLE_ALIGN_H) ||
557 gnm_style_is_element_set (style, MSTYLE_ALIGN_V) ||
558 gnm_style_is_element_set (style, MSTYLE_FONT_STRIKETHROUGH) ||
559 gnm_style_is_element_set (style, MSTYLE_FONT_UNDERLINE) ||
560 gnm_style_is_element_set (style, MSTYLE_FONT_COLOR);
562 if (row_height)
563 res |= GNM_SPANCALC_ROW_HEIGHT;
564 if (format_change || size_change)
565 res |= GNM_SPANCALC_RE_RENDER | GNM_SPANCALC_RESIZE;
567 return res;
571 * gnm_style_default_halign:
572 * @style:
573 * @c:
575 * Select the appropriate horizontal alignment depending on the style and cell
576 * value.
578 GnmHAlign
579 gnm_style_default_halign (GnmStyle const *style, GnmCell const *c)
581 GnmHAlign align = gnm_style_get_align_h (style);
582 GnmValue *v;
584 if (align != GNM_HALIGN_GENERAL)
585 return align;
586 g_return_val_if_fail (c != NULL, GNM_HALIGN_RIGHT);
588 if (c->base.sheet && c->base.sheet->display_formulas &&
589 gnm_cell_has_expr (c))
590 return GNM_HALIGN_LEFT;
592 for (v = c->value; v != NULL ; )
593 switch (v->v_any.type) {
594 case VALUE_BOOLEAN:
595 case VALUE_ERROR:
596 return GNM_HALIGN_CENTER;
598 case VALUE_FLOAT: {
599 double a = gnm_style_get_rotation (style);
600 if (a > 0 && a < 180)
601 return GNM_HALIGN_LEFT;
602 return GNM_HALIGN_RIGHT;
605 case VALUE_ARRAY:
606 /* Tail recurse into the array */
607 if (v->v_array.x > 0 && v->v_array.y > 0) {
608 v = v->v_array.vals [0][0];
609 continue;
612 default:
613 if (gnm_style_get_rotation (style) > 180)
614 return GNM_HALIGN_RIGHT;
615 return GNM_HALIGN_LEFT;
617 return GNM_HALIGN_RIGHT;
620 PangoUnderline
621 gnm_translate_underline_to_pango (GnmUnderline ul)
623 g_return_val_if_fail (ul >= UNDERLINE_NONE, PANGO_UNDERLINE_NONE);
624 g_return_val_if_fail (ul <= UNDERLINE_DOUBLE_LOW, PANGO_UNDERLINE_NONE);
626 switch (ul) {
627 case UNDERLINE_SINGLE:
628 return PANGO_UNDERLINE_SINGLE;
629 case UNDERLINE_DOUBLE:
630 case UNDERLINE_DOUBLE_LOW:
631 return PANGO_UNDERLINE_DOUBLE;
632 case UNDERLINE_SINGLE_LOW:
633 return PANGO_UNDERLINE_LOW;
634 case UNDERLINE_NONE:
635 default:
636 return PANGO_UNDERLINE_NONE;
640 GnmUnderline
641 gnm_translate_underline_from_pango (PangoUnderline pul)
643 g_return_val_if_fail (pul >= PANGO_UNDERLINE_NONE, UNDERLINE_NONE);
644 g_return_val_if_fail (pul <= PANGO_UNDERLINE_ERROR, UNDERLINE_NONE);
646 switch (pul) {
647 case PANGO_UNDERLINE_SINGLE:
648 return UNDERLINE_SINGLE;
649 case PANGO_UNDERLINE_DOUBLE:
650 return UNDERLINE_DOUBLE;
651 case PANGO_UNDERLINE_LOW:
652 return UNDERLINE_SINGLE_LOW;
653 case PANGO_UNDERLINE_ERROR:
654 /* What? */
655 case PANGO_UNDERLINE_NONE:
656 default:
657 return UNDERLINE_NONE;