]> icculus.org git repositories - mikachu/openbox.git/blob - obrender/font.c
Remove incorrect assertion for MoveResizeTo
[mikachu/openbox.git] / obrender / 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     static PangoLanguage *lang = NULL;
38
39     if (lang == NULL) {
40 #if PANGO_VERSION_MAJOR > 1 || \
41     (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
42         lang = pango_language_get_default();
43 #else
44         gchar *locale, *p;
45         /* get the default language from the locale
46            (based on gtk_get_default_language in gtkmain.c) */
47         locale = g_strdup(setlocale(LC_CTYPE, NULL));
48         if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
49         if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
50         lang = pango_language_from_string(locale);
51         g_free(locale);
52 #endif
53     }
54
55     /* measure the ascent and descent */
56     metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang);
57     f->ascent = pango_font_metrics_get_ascent(metrics);
58     f->descent = pango_font_metrics_get_descent(metrics);
59     pango_font_metrics_unref(metrics);
60
61 }
62
63 void RrFontDescriptionFromString(RrFont *font, gchar *description)
64 {
65     PangoFontDescription *desc;
66     desc = pango_font_description_from_string(description);
67     pango_font_description_merge(font->font_desc, desc, TRUE);
68     pango_font_description_free(desc);
69     pango_layout_set_font_description(font->layout, font->font_desc);
70 }
71
72 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
73                    RrFontWeight weight, RrFontSlant slant)
74 {
75     RrFont *out;
76     PangoWeight pweight;
77     PangoStyle pstyle;
78     PangoAttrList *attrlist;
79
80     out = g_slice_new(RrFont);
81     out->inst = inst;
82     out->ref = 1;
83     out->font_desc = pango_font_description_new();
84     out->layout = pango_layout_new(inst->pango);
85     out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_SINGLE);
86     out->shortcut_underline->start_index = 0;
87     out->shortcut_underline->end_index = 0;
88
89     attrlist = pango_attr_list_new();
90     /* shortcut_underline is owned by the attrlist */
91     pango_attr_list_insert(attrlist, out->shortcut_underline);
92     /* the attributes are owned by the layout */
93     pango_layout_set_attributes(out->layout, attrlist);
94     pango_attr_list_unref(attrlist);
95
96     switch (weight) {
97     case RR_FONTWEIGHT_LIGHT:     pweight = PANGO_WEIGHT_LIGHT;     break;
98     case RR_FONTWEIGHT_NORMAL:    pweight = PANGO_WEIGHT_NORMAL;    break;
99     case RR_FONTWEIGHT_SEMIBOLD:  pweight = PANGO_WEIGHT_SEMIBOLD;  break;
100     case RR_FONTWEIGHT_BOLD:      pweight = PANGO_WEIGHT_BOLD;      break;
101     case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
102     default: g_assert_not_reached();
103     }
104
105     switch (slant) {
106     case RR_FONTSLANT_NORMAL:  pstyle = PANGO_STYLE_NORMAL;    break;
107     case RR_FONTSLANT_ITALIC:  pstyle = PANGO_STYLE_ITALIC;    break;
108     case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE;   break;
109     default: g_assert_not_reached();
110     }
111
112     /* setup the font */
113     pango_font_description_set_family(out->font_desc, name);
114     pango_font_description_set_weight(out->font_desc, pweight);
115     pango_font_description_set_style(out->font_desc, pstyle);
116     if (size < 0)
117         pango_font_description_set_absolute_size(out->font_desc, -size * PANGO_SCALE);
118     else
119         pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
120
121     /* setup the layout */
122     pango_layout_set_font_description(out->layout, out->font_desc);
123     pango_layout_set_wrap(out->layout, PANGO_WRAP_WORD_CHAR);
124
125     /* get the ascent and descent */
126     measure_font(inst, out);
127
128     return out;
129 }
130
131 RrFont *RrFontOpenDefault(const RrInstance *inst)
132 {
133     return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
134                       RrDefaultFontWeight, RrDefaultFontSlant);
135 }
136
137 void RrFontRef(RrFont *f)
138 {
139     ++f->ref;
140 }
141
142 void RrFontClose(RrFont *f)
143 {
144     if (f) {
145         if (--f->ref < 1) {
146             g_object_unref(f->layout);
147             pango_font_description_free(f->font_desc);
148             g_slice_free(RrFont, f);
149         }
150     }
151 }
152
153 static void font_measure_full(const RrFont *f, const gchar *str,
154                               gint *x, gint *y, gint shadow_x, gint shadow_y,
155                               gboolean flow, gint maxwidth)
156 {
157     PangoRectangle rect;
158
159     pango_layout_set_text(f->layout, str, -1);
160     if (flow) {
161         pango_layout_set_single_paragraph_mode(f->layout, FALSE);
162         pango_layout_set_width(f->layout, maxwidth * PANGO_SCALE);
163         pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_NONE);
164     }
165     else {
166         /* single line mode */
167         pango_layout_set_single_paragraph_mode(f->layout, TRUE);
168         pango_layout_set_width(f->layout, -1);
169         pango_layout_set_ellipsize(f->layout, PANGO_ELLIPSIZE_MIDDLE);
170     }
171
172     /* pango_layout_get_pixel_extents lies! this is the right way to get the
173        size of the text's area */
174     pango_layout_get_extents(f->layout, NULL, &rect);
175 #if PANGO_VERSION_MAJOR > 1 || \
176     (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
177     /* pass the logical rect as the ink rect, this is on purpose so we get the
178        full area for the text */
179     pango_extents_to_pixels(&rect, NULL);
180 #else
181     rect.width = (rect.width + PANGO_SCALE - 1) / PANGO_SCALE;
182     rect.height = (rect.height + PANGO_SCALE - 1) / PANGO_SCALE;
183 #endif
184     *x = rect.width + /* ABS(shadow_x) +*/ 4 /* we put a 2 px edge on each side */;
185     *y = rect.height /*+ ABS(shadow_y) */;
186 }
187
188 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
189                             gint shadow_x, gint shadow_y,
190                             gboolean flow, gint maxwidth)
191 {
192     RrSize *size;
193
194     g_assert(!flow || maxwidth > 0);
195
196     size = g_slice_new(RrSize);
197     font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y,
198                       flow, maxwidth);
199     return size;
200 }
201
202 gint RrFontHeight(const RrFont *f, gint shadow_y)
203 {
204     return (f->ascent + f->descent) / PANGO_SCALE + 0-1;//ABS(shadow_y);
205 }
206
207 static inline int font_calculate_baseline(RrFont *f, gint height)
208 {
209 /* For my own reference:
210  *   _________
211  *  ^space/2  ^height     ^baseline
212  *  v_________|_          |
213  *            | ^ascent   |   _           _
214  *            | |         |  | |_ _____ _| |_ _  _
215  *            | |         |  |  _/ -_) \ /  _| || |
216  *            | v_________v   \__\___/_\_\\__|\_, |
217  *            | ^descent                      |__/
218  *  __________|_v
219  *  ^space/2  |
220  *  V_________v
221  */
222     return (((height * PANGO_SCALE) /* height of the space in pango units */
223              - (f->ascent + f->descent)) /* minus space taken up by text */
224             / 2 /* divided by two -> half of the empty space (this is the top
225                    of the text) */
226             + f->ascent) /* now move down to the baseline */
227         / PANGO_SCALE; /* back to pixels */
228 }
229
230 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
231 {
232     gint x,y,w;
233     XftColor c;
234     gint mw;
235     PangoRectangle rect;
236     PangoAttrList *attrlist;
237     PangoEllipsizeMode ell;
238
239     g_assert(!t->flow || t->maxwidth > 0);
240
241     y = area->y;
242     if (!t->flow)
243         /* center the text vertically
244            We do this centering based on the 'baseline' since different fonts
245            have different top edges. It looks bad when the whole string is
246            moved when 1 character from a non-default language is included in
247            the string */
248         y += font_calculate_baseline(t->font, area->height);
249
250     /* the +2 and -4 leave a small blank edge on the sides */
251     x = area->x + 2;
252     w = area->width;
253     if (t->flow) w = MAX(w, t->maxwidth);
254     w -= 4;
255     /* h = area->height; */
256
257     if (t->flow)
258         ell = PANGO_ELLIPSIZE_NONE;
259     else {
260         switch (t->ellipsize) {
261         case RR_ELLIPSIZE_NONE:
262             ell = PANGO_ELLIPSIZE_NONE;
263             break;
264         case RR_ELLIPSIZE_START:
265             ell = PANGO_ELLIPSIZE_START;
266             break;
267         case RR_ELLIPSIZE_MIDDLE:
268             ell = PANGO_ELLIPSIZE_MIDDLE;
269             break;
270         case RR_ELLIPSIZE_END:
271             ell = PANGO_ELLIPSIZE_END;
272             break;
273         default:
274             g_assert_not_reached();
275         }
276     }
277
278     pango_layout_set_text(t->font->layout, t->string, -1);
279     pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
280     pango_layout_set_ellipsize(t->font->layout, ell);
281     pango_layout_set_single_paragraph_mode(t->font->layout, !t->flow);
282
283     /* * * end of setting up the layout * * */
284
285     pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
286     mw = rect.width;
287
288     /* pango_layout_set_alignment doesn't work with
289        pango_xft_render_layout_line */
290     switch (t->justify) {
291     case RR_JUSTIFY_LEFT:
292         break;
293     case RR_JUSTIFY_RIGHT:
294         x += (w - mw);
295         break;
296     case RR_JUSTIFY_CENTER:
297         x += (w - mw) / 2;
298         break;
299     case RR_JUSTIFY_NUM_TYPES:
300         g_assert_not_reached();
301     }
302
303     if (t->shadow_offset_x || t->shadow_offset_y) {
304         /* From nvidia's readme (chapter 23):
305
306            When rendering to a 32-bit window, keep in mind that the X RENDER
307            extension, used by most composite managers, expects "premultiplied
308            alpha" colors. This means that if your color has components (r,g,b)
309            and alpha value a, then you must render (a*r, a*g, a*b, a) into the
310            target window.
311         */
312         c.color.red = (t->shadow_color->r | t->shadow_color->r << 8) *
313             t->shadow_alpha / 255;
314         c.color.green = (t->shadow_color->g | t->shadow_color->g << 8) *
315             t->shadow_alpha / 255;
316         c.color.blue = (t->shadow_color->b | t->shadow_color->b << 8) *
317             t->shadow_alpha / 255;
318         c.color.alpha = 0xffff * t->shadow_alpha / 255;
319         c.pixel = t->shadow_color->pixel;
320
321         /* see below... */
322         if (!t->flow) {
323             pango_xft_render_layout_line
324                 (d, &c,
325 #if PANGO_VERSION_MAJOR > 1 || \
326     (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
327                  pango_layout_get_line_readonly(t->font->layout, 0),
328 #else
329                  pango_layout_get_line(t->font->layout, 0),
330 #endif
331                  (x + t->shadow_offset_x) * PANGO_SCALE,
332                  (y + t->shadow_offset_y) * PANGO_SCALE);
333         }
334         else {
335             pango_xft_render_layout(d, &c, t->font->layout,
336                                     (x + t->shadow_offset_x) * PANGO_SCALE,
337                                     (y + t->shadow_offset_y) * PANGO_SCALE);
338         }
339     }
340
341     c.color.red = t->color->r | t->color->r << 8;
342     c.color.green = t->color->g | t->color->g << 8;
343     c.color.blue = t->color->b | t->color->b << 8;
344     c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
345     c.pixel = t->color->pixel;
346
347     if (t->shortcut) {
348         const gchar *s = t->string + t->shortcut_pos;
349
350         t->font->shortcut_underline->start_index = t->shortcut_pos;
351         t->font->shortcut_underline->end_index = t->shortcut_pos +
352             (g_utf8_next_char(s) - s);
353
354         /* the attributes are owned by the layout.
355            re-add the attributes to the layout after changing the
356            start and end index */
357         attrlist = pango_layout_get_attributes(t->font->layout);
358         pango_attr_list_ref(attrlist);
359         pango_layout_set_attributes(t->font->layout, attrlist);
360         pango_attr_list_unref(attrlist);
361     }
362
363     /* layout_line() uses y to specify the baseline
364        The line doesn't need to be freed, it's a part of the layout */
365     if (!t->flow) {
366         pango_xft_render_layout_line
367             (d, &c,
368 #if PANGO_VERSION_MAJOR > 1 || \
369     (PANGO_VERSION_MAJOR == 1 && PANGO_VERSION_MINOR >= 16)
370              pango_layout_get_line_readonly(t->font->layout, 0),
371 #else
372              pango_layout_get_line(t->font->layout, 0),
373 #endif
374              x * PANGO_SCALE,
375              y * PANGO_SCALE);
376     }
377     else {
378         pango_xft_render_layout(d, &c, t->font->layout,
379                                 x * PANGO_SCALE,
380                                 y * PANGO_SCALE);
381     }
382
383     if (t->shortcut) {
384         t->font->shortcut_underline->start_index = 0;
385         t->font->shortcut_underline->end_index = 0;
386         /* the attributes are owned by the layout.
387            re-add the attributes to the layout after changing the
388            start and end index */
389         attrlist = pango_layout_get_attributes(t->font->layout);
390         pango_attr_list_ref(attrlist);
391         pango_layout_set_attributes(t->font->layout, attrlist);
392         pango_attr_list_unref(attrlist);
393     }
394 }