]> icculus.org git repositories - mikachu/openbox.git/blob - render/font.c
fix using uninit'd values
[mikachu/openbox.git] / render / font.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    font.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6    Copyright (c) 2003        Derek Foreman
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    See the COPYING file for a copy of the GNU General Public License.
19 */
20
21 #include "font.h"
22 #include "color.h"
23 #include "mask.h"
24 #include "theme.h"
25 #include "geom.h"
26 #include "instance.h"
27 #include "gettext.h"
28
29 #include <glib.h>
30 #include <string.h>
31 #include <stdlib.h>
32 #include <locale.h>
33
34 static void measure_font(const RrInstance *inst, RrFont *f)
35 {
36     PangoFontMetrics *metrics;
37     gchar *locale, *p;
38
39     /* get the default language from the locale
40        (based on gtk_get_default_language in gtkmain.c) */
41     locale = g_strdup(setlocale(LC_CTYPE, NULL));
42     if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
43     if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
44
45     /* measure the ascent and descent */
46     metrics = pango_context_get_metrics(inst->pango, f->font_desc,
47                                         pango_language_from_string(locale));
48     f->ascent = pango_font_metrics_get_ascent(metrics);
49     f->descent = pango_font_metrics_get_descent(metrics);
50     pango_font_metrics_unref(metrics);
51
52     g_free(locale);
53 }
54
55 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
56                    RrFontWeight weight, RrFontSlant slant)
57 {
58     RrFont *out;
59     PangoWeight pweight;
60     PangoStyle pstyle;
61     PangoAttrList *attrlist;
62
63     out = g_new(RrFont, 1);
64     out->inst = inst;
65     out->ref = 1;
66     out->font_desc = pango_font_description_new();
67     out->layout = pango_layout_new(inst->pango);
68     out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
69     out->shortcut_underline->start_index = 0;
70     out->shortcut_underline->end_index = 0;
71
72     attrlist = pango_attr_list_new();
73     /* shortcut_underline is owned by the attrlist */
74     pango_attr_list_insert(attrlist, out->shortcut_underline);
75     /* the attributes are owned by the layout */
76     pango_layout_set_attributes(out->layout, attrlist);
77     pango_attr_list_unref(attrlist);
78
79     switch (weight) {
80     case RR_FONTWEIGHT_LIGHT:     pweight = PANGO_WEIGHT_LIGHT;     break;
81     case RR_FONTWEIGHT_NORMAL:    pweight = PANGO_WEIGHT_NORMAL;    break;
82     case RR_FONTWEIGHT_SEMIBOLD:  pweight = PANGO_WEIGHT_SEMIBOLD;  break;
83     case RR_FONTWEIGHT_BOLD:      pweight = PANGO_WEIGHT_BOLD;      break;
84     case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
85     default: g_assert_not_reached();
86     }
87
88     switch (slant) {
89     case RR_FONTSLANT_NORMAL:  pstyle = PANGO_STYLE_NORMAL;    break;
90     case RR_FONTSLANT_ITALIC:  pstyle = PANGO_STYLE_ITALIC;    break;
91     case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE;   break;
92     default: g_assert_not_reached();
93     }
94
95     /* setup the font */
96     pango_font_description_set_family(out->font_desc, name);
97     pango_font_description_set_weight(out->font_desc, pweight);
98     pango_font_description_set_style(out->font_desc, pstyle);
99     pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
100
101     /* setup the layout */
102     pango_layout_set_font_description(out->layout, out->font_desc);
103     pango_layout_set_single_paragraph_mode(out->layout, TRUE);
104     pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
105
106     /* get the ascent and descent */
107     measure_font(inst, out);
108
109     return out;
110 }
111
112 RrFont *RrFontOpenDefault(const RrInstance *inst)
113 {
114     return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
115                       RrDefaultFontWeight, RrDefaultFontSlant);
116 }
117
118 void RrFontRef(RrFont *f)
119 {
120     ++f->ref;
121 }
122
123 void RrFontClose(RrFont *f)
124 {
125     if (f) {
126         if (--f->ref < 1) {
127             g_object_unref(f->layout);
128             pango_font_description_free(f->font_desc);
129             g_free(f);
130         }
131     }
132 }
133
134 static void font_measure_full(const RrFont *f, const gchar *str,
135                               gint *x, gint *y, gint shadow_x, gint shadow_y)
136 {
137     PangoRectangle rect;
138
139     pango_layout_set_text(f->layout, str, -1);
140     pango_layout_set_width(f->layout, -1);
141     pango_layout_get_pixel_extents(f->layout, NULL, &rect);
142     *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
143     *y = rect.height + ABS(shadow_y);
144 }
145
146 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
147                             gint shadow_x, gint shadow_y)
148 {
149     RrSize *size;
150     size = g_new(RrSize, 1);
151     font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y);
152     return size;
153 }
154
155 gint RrFontHeight(const RrFont *f, gint shadow_y)
156 {
157     return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
158 }
159
160 static inline int font_calculate_baseline(RrFont *f, gint height)
161 {
162 /* For my own reference:
163  *   _________
164  *  ^space/2  ^height     ^baseline
165  *  v_________|_          |
166  *            | ^ascent   |   _           _
167  *            | |         |  | |_ _____ _| |_ _  _
168  *            | |         |  |  _/ -_) \ /  _| || |
169  *            | v_________v   \__\___/_\_\\__|\_, |
170  *            | ^descent                      |__/
171  *  __________|_v
172  *  ^space/2  |
173  *  V_________v
174  */
175     return (((height * PANGO_SCALE) /* height of the space in pango units */
176              - (f->ascent + f->descent)) /* minus space taken up by text */
177             / 2 /* divided by two -> half of the empty space (this is the top
178                    of the text) */
179             + f->ascent) /* now move down to the baseline */
180         / PANGO_SCALE; /* back to pixels */
181 }
182
183 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
184 {
185     gint x,y,w,h;
186     XftColor c;
187     gint mw;
188     PangoRectangle rect;
189     PangoAttrList *attrlist;
190
191     /* center the text vertically
192        We do this centering based on the 'baseline' since different fonts have
193        different top edges. It looks bad when the whole string is moved when 1
194        character from a non-default language is included in the string */
195     y = area->y +
196         font_calculate_baseline(t->font, area->height);
197
198     /* the +2 and -4 leave a small blank edge on the sides */
199     x = area->x + 2;
200     w = area->width - 4;
201     h = area->height;
202
203     pango_layout_set_text(t->font->layout, t->string, -1);
204     pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
205
206     /* * * end of setting up the layout * * */
207
208     pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
209     mw = rect.width;
210
211     /* pango_layout_set_alignment doesn't work with 
212        pango_xft_render_layout_line */
213     switch (t->justify) {
214     case RR_JUSTIFY_LEFT:
215         break;
216     case RR_JUSTIFY_RIGHT:
217         x += (w - mw);
218         break;
219     case RR_JUSTIFY_CENTER:
220         x += (w - mw) / 2;
221         break;
222     }
223
224     if (t->shadow_offset_x || t->shadow_offset_y) {
225         c.color.red = t->shadow_color->r | t->shadow_color->r << 8;
226         c.color.green = t->shadow_color->g | t->shadow_color->g << 8;
227         c.color.blue = t->shadow_color->b | t->shadow_color->b << 8;
228         c.color.alpha = 0xffff * t->shadow_alpha / 255;
229         c.pixel = t->shadow_color->pixel;
230
231         /* see below... */
232         pango_xft_render_layout_line
233             (d, &c, pango_layout_get_line(t->font->layout, 0),
234              (x + t->shadow_offset_x) * PANGO_SCALE,
235              (y + t->shadow_offset_y) * PANGO_SCALE);
236     }
237
238     c.color.red = t->color->r | t->color->r << 8;
239     c.color.green = t->color->g | t->color->g << 8;
240     c.color.blue = t->color->b | t->color->b << 8;
241     c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
242     c.pixel = t->color->pixel;
243
244     if (t->shortcut) {
245         const gchar *c = t->string + t->shortcut_pos;
246
247         t->font->shortcut_underline->start_index = t->shortcut_pos;
248         t->font->shortcut_underline->end_index = t->shortcut_pos +
249             (g_utf8_next_char(c) - c);
250
251         /* the attributes are owned by the layout.
252            re-add the attributes to the layout after changing the
253            start and end index */
254         attrlist = pango_layout_get_attributes(t->font->layout);
255         pango_attr_list_ref(attrlist);
256         pango_layout_set_attributes(t->font->layout, attrlist);
257         pango_attr_list_unref(attrlist);
258     }
259
260     /* layout_line() uses y to specify the baseline
261        The line doesn't need to be freed, it's a part of the layout */
262     pango_xft_render_layout_line
263         (d, &c, pango_layout_get_line(t->font->layout, 0),
264          x * PANGO_SCALE, y * PANGO_SCALE);
265
266     if (t->shortcut) {
267         t->font->shortcut_underline->start_index = 0;
268         t->font->shortcut_underline->end_index = 0;
269         /* the attributes are owned by the layout.
270            re-add the attributes to the layout after changing the
271            start and end index */
272         attrlist = pango_layout_get_attributes(t->font->layout);
273         pango_attr_list_ref(attrlist);
274         pango_layout_set_attributes(t->font->layout, attrlist);
275         pango_attr_list_unref(attrlist);
276     }
277 }