]> icculus.org git repositories - mikachu/openbox.git/blob - render/font.c
dont count button releases to change the context until the first button pressed is...
[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     static PangoLanguage *lang = NULL;
38
39     if (lang == NULL) {
40 #if PANGO_VERSION_CHECK(1,16,0)
41         lang = pango_language_get_default();
42 #else
43         gchar *locale, *p;
44         /* get the default language from the locale
45            (based on gtk_get_default_language in gtkmain.c) */
46         locale = g_strdup(setlocale(LC_CTYPE, NULL));
47         if ((p = strchr(locale, '.'))) *p = '\0'; /* strip off the . */
48         if ((p = strchr(locale, '@'))) *p = '\0'; /* strip off the @ */
49         lang = pango_language_from_string(locale);
50         g_free(locale);
51 #endif
52     }
53
54     /* measure the ascent and descent */
55     metrics = pango_context_get_metrics(inst->pango, f->font_desc, lang);
56     f->ascent = pango_font_metrics_get_ascent(metrics);
57     f->descent = pango_font_metrics_get_descent(metrics);
58     pango_font_metrics_unref(metrics);
59
60 }
61
62 RrFont *RrFontOpen(const RrInstance *inst, const gchar *name, gint size,
63                    RrFontWeight weight, RrFontSlant slant)
64 {
65     RrFont *out;
66     PangoWeight pweight;
67     PangoStyle pstyle;
68     PangoAttrList *attrlist;
69
70     out = g_new(RrFont, 1);
71     out->inst = inst;
72     out->ref = 1;
73     out->font_desc = pango_font_description_new();
74     out->layout = pango_layout_new(inst->pango);
75     out->shortcut_underline = pango_attr_underline_new(PANGO_UNDERLINE_LOW);
76     out->shortcut_underline->start_index = 0;
77     out->shortcut_underline->end_index = 0;
78
79     attrlist = pango_attr_list_new();
80     /* shortcut_underline is owned by the attrlist */
81     pango_attr_list_insert(attrlist, out->shortcut_underline);
82     /* the attributes are owned by the layout */
83     pango_layout_set_attributes(out->layout, attrlist);
84     pango_attr_list_unref(attrlist);
85
86     switch (weight) {
87     case RR_FONTWEIGHT_LIGHT:     pweight = PANGO_WEIGHT_LIGHT;     break;
88     case RR_FONTWEIGHT_NORMAL:    pweight = PANGO_WEIGHT_NORMAL;    break;
89     case RR_FONTWEIGHT_SEMIBOLD:  pweight = PANGO_WEIGHT_SEMIBOLD;  break;
90     case RR_FONTWEIGHT_BOLD:      pweight = PANGO_WEIGHT_BOLD;      break;
91     case RR_FONTWEIGHT_ULTRABOLD: pweight = PANGO_WEIGHT_ULTRABOLD; break;
92     default: g_assert_not_reached();
93     }
94
95     switch (slant) {
96     case RR_FONTSLANT_NORMAL:  pstyle = PANGO_STYLE_NORMAL;    break;
97     case RR_FONTSLANT_ITALIC:  pstyle = PANGO_STYLE_ITALIC;    break;
98     case RR_FONTSLANT_OBLIQUE: pstyle = PANGO_STYLE_OBLIQUE;   break;
99     default: g_assert_not_reached();
100     }
101
102     /* setup the font */
103     pango_font_description_set_family(out->font_desc, name);
104     pango_font_description_set_weight(out->font_desc, pweight);
105     pango_font_description_set_style(out->font_desc, pstyle);
106     pango_font_description_set_size(out->font_desc, size * PANGO_SCALE);
107
108     /* setup the layout */
109     pango_layout_set_font_description(out->layout, out->font_desc);
110     pango_layout_set_single_paragraph_mode(out->layout, TRUE);
111     pango_layout_set_ellipsize(out->layout, PANGO_ELLIPSIZE_MIDDLE);
112
113     /* get the ascent and descent */
114     measure_font(inst, out);
115
116     return out;
117 }
118
119 RrFont *RrFontOpenDefault(const RrInstance *inst)
120 {
121     return RrFontOpen(inst, RrDefaultFontFamily, RrDefaultFontSize,
122                       RrDefaultFontWeight, RrDefaultFontSlant);
123 }
124
125 void RrFontRef(RrFont *f)
126 {
127     ++f->ref;
128 }
129
130 void RrFontClose(RrFont *f)
131 {
132     if (f) {
133         if (--f->ref < 1) {
134             g_object_unref(f->layout);
135             pango_font_description_free(f->font_desc);
136             g_free(f);
137         }
138     }
139 }
140
141 static void font_measure_full(const RrFont *f, const gchar *str,
142                               gint *x, gint *y, gint shadow_x, gint shadow_y)
143 {
144     PangoRectangle rect;
145
146     pango_layout_set_text(f->layout, str, -1);
147     pango_layout_set_width(f->layout, -1);
148     pango_layout_get_pixel_extents(f->layout, NULL, &rect);
149     *x = rect.width + ABS(shadow_x) + 4 /* we put a 2 px edge on each side */;
150     *y = rect.height + ABS(shadow_y);
151 }
152
153 RrSize *RrFontMeasureString(const RrFont *f, const gchar *str,
154                             gint shadow_x, gint shadow_y)
155 {
156     RrSize *size;
157     size = g_new(RrSize, 1);
158     font_measure_full(f, str, &size->width, &size->height, shadow_x, shadow_y);
159     return size;
160 }
161
162 gint RrFontHeight(const RrFont *f, gint shadow_y)
163 {
164     return (f->ascent + f->descent) / PANGO_SCALE + ABS(shadow_y);
165 }
166
167 static inline int font_calculate_baseline(RrFont *f, gint height)
168 {
169 /* For my own reference:
170  *   _________
171  *  ^space/2  ^height     ^baseline
172  *  v_________|_          |
173  *            | ^ascent   |   _           _
174  *            | |         |  | |_ _____ _| |_ _  _
175  *            | |         |  |  _/ -_) \ /  _| || |
176  *            | v_________v   \__\___/_\_\\__|\_, |
177  *            | ^descent                      |__/
178  *  __________|_v
179  *  ^space/2  |
180  *  V_________v
181  */
182     return (((height * PANGO_SCALE) /* height of the space in pango units */
183              - (f->ascent + f->descent)) /* minus space taken up by text */
184             / 2 /* divided by two -> half of the empty space (this is the top
185                    of the text) */
186             + f->ascent) /* now move down to the baseline */
187         / PANGO_SCALE; /* back to pixels */
188 }
189
190 void RrFontDraw(XftDraw *d, RrTextureText *t, RrRect *area)
191 {
192     gint x,y,w,h;
193     XftColor c;
194     gint mw;
195     PangoRectangle rect;
196     PangoAttrList *attrlist;
197
198     /* center the text vertically
199        We do this centering based on the 'baseline' since different fonts have
200        different top edges. It looks bad when the whole string is moved when 1
201        character from a non-default language is included in the string */
202     y = area->y +
203         font_calculate_baseline(t->font, area->height);
204
205     /* the +2 and -4 leave a small blank edge on the sides */
206     x = area->x + 2;
207     w = area->width - 4;
208     h = area->height;
209
210     pango_layout_set_text(t->font->layout, t->string, -1);
211     pango_layout_set_width(t->font->layout, w * PANGO_SCALE);
212
213     /* * * end of setting up the layout * * */
214
215     pango_layout_get_pixel_extents(t->font->layout, NULL, &rect);
216     mw = rect.width;
217
218     /* pango_layout_set_alignment doesn't work with 
219        pango_xft_render_layout_line */
220     switch (t->justify) {
221     case RR_JUSTIFY_LEFT:
222         break;
223     case RR_JUSTIFY_RIGHT:
224         x += (w - mw);
225         break;
226     case RR_JUSTIFY_CENTER:
227         x += (w - mw) / 2;
228         break;
229     }
230
231     if (t->shadow_offset_x || t->shadow_offset_y) {
232         c.color.red = t->shadow_color->r | t->shadow_color->r << 8;
233         c.color.green = t->shadow_color->g | t->shadow_color->g << 8;
234         c.color.blue = t->shadow_color->b | t->shadow_color->b << 8;
235         c.color.alpha = 0xffff * t->shadow_alpha / 255;
236         c.pixel = t->shadow_color->pixel;
237
238         /* see below... */
239         pango_xft_render_layout_line
240             (d, &c, pango_layout_get_line(t->font->layout, 0),
241              (x + t->shadow_offset_x) * PANGO_SCALE,
242              (y + t->shadow_offset_y) * PANGO_SCALE);
243     }
244
245     c.color.red = t->color->r | t->color->r << 8;
246     c.color.green = t->color->g | t->color->g << 8;
247     c.color.blue = t->color->b | t->color->b << 8;
248     c.color.alpha = 0xff | 0xff << 8; /* fully opaque text */
249     c.pixel = t->color->pixel;
250
251     if (t->shortcut) {
252         const gchar *c = t->string + t->shortcut_pos;
253
254         t->font->shortcut_underline->start_index = t->shortcut_pos;
255         t->font->shortcut_underline->end_index = t->shortcut_pos +
256             (g_utf8_next_char(c) - c);
257
258         /* the attributes are owned by the layout.
259            re-add the attributes to the layout after changing the
260            start and end index */
261         attrlist = pango_layout_get_attributes(t->font->layout);
262         pango_attr_list_ref(attrlist);
263         pango_layout_set_attributes(t->font->layout, attrlist);
264         pango_attr_list_unref(attrlist);
265     }
266
267     /* layout_line() uses y to specify the baseline
268        The line doesn't need to be freed, it's a part of the layout */
269     pango_xft_render_layout_line
270         (d, &c, pango_layout_get_line(t->font->layout, 0),
271          x * PANGO_SCALE, y * PANGO_SCALE);
272
273     if (t->shortcut) {
274         t->font->shortcut_underline->start_index = 0;
275         t->font->shortcut_underline->end_index = 0;
276         /* the attributes are owned by the layout.
277            re-add the attributes to the layout after changing the
278            start and end index */
279         attrlist = pango_layout_get_attributes(t->font->layout);
280         pango_attr_list_ref(attrlist);
281         pango_layout_set_attributes(t->font->layout, attrlist);
282         pango_attr_list_unref(attrlist);
283     }
284 }