]> icculus.org git repositories - dana/openbox.git/blob - src/Font.cc
make the styles' doJustify methods stop returning the string length as this is no...
[dana/openbox.git] / src / Font.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Font.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
5 //
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
12 //
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
15 //
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
23
24 #ifdef    HAVE_CONFIG_H
25 #  include "../config.h"
26 #endif // HAVE_CONFIG_H
27
28 extern "C" {
29 #ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 #endif // HAVE_STDLIB_H
32 }
33
34 #include <iostream>
35 #include <algorithm>
36
37 using std::string;
38 using std::cerr;
39 using std::endl;
40
41 #include "i18n.hh"
42 #include "Font.hh"
43 #include "Util.hh"
44 #include "GCCache.hh"
45 #include "Color.hh"
46
47 //bool        BFont::_antialias       = False;
48 string      BFont::_fallback_font   = "fixed";
49
50
51 BFont::BFont(Display *d, BScreen *screen, const string &family, int size,
52              bool bold, bool italic) : _display(d),
53                                        _screen(screen),
54                                        _name(family),
55                                        _simplename(False),
56                                        _size(size * 10),
57                                        _bold(bold),
58                                        _italic(italic),
59 #ifdef    XFT
60                                        _xftfont(0),
61 #endif // XFT
62                                        _font(0),
63                                        _fontset(0),
64                                        _fontset_extents(0),
65                                        _cache(0),
66                                        _item(0) {
67   _valid = init();
68 }
69
70
71 BFont::BFont(Display *d, BScreen *screen, const string &xlfd) :
72                                        _display(d),
73                                        _screen(screen),
74 #ifdef    XFT
75                                        _xftfont(0),
76 #endif // XFT
77                                        _font(0),
78                                        _fontset(0),
79                                        _fontset_extents(0),
80                                        _cache(0),
81                                        _item(0) {
82   string int_xlfd;
83   if (xlfd.empty())
84     int_xlfd = _fallback_font;
85   else
86     int_xlfd = xlfd;
87   
88   _valid = init(xlfd);
89 }
90
91
92 bool BFont::init(const string &xlfd) {
93   // try load the specified font
94   if (xlfd.empty() || parseFontString(xlfd))
95     if (createFont())
96       return True;
97
98   if (xlfd != _fallback_font) {
99     // try the fallback
100     cerr << "BFont::BFont(): couldn't load font '" << _name << "'" << endl <<
101       "Falling back to default '" << _fallback_font << "'" << endl;
102   if (parseFontString(_fallback_font))
103     if (createFont())
104       return True;
105   }
106
107   cerr << "BFont::BFont(): couldn't load font '" << _name << "'" << endl <<
108     "Giving up!" << endl;
109   
110   return False;
111 }
112
113
114 bool BFont::createFont(void) {
115   std::string fullname;
116
117 #ifdef    XFT
118   fullname = buildXlfdName(False);
119   _xftfont = XftFontOpenXlfd(_display, _screen->getScreenNumber(),
120                              fullname.c_str());
121   if (_xftfont)
122     return True;
123
124   cerr << "BFont::BFont(): couldn't load font '" << _name << "'" << endl <<
125     "as an Xft font, trying as a standard X font." << endl;
126 #endif
127
128   if (i18n.multibyte()) {
129     char **missing, *def = "-";
130     int nmissing;
131   
132     fullname = buildXlfdName(True);
133     _fontset = XCreateFontSet(_display, fullname.c_str(), &missing, &nmissing,
134                               &def);
135     if (nmissing) XFreeStringList(missing);
136     if (_fontset)
137       _fontset_extents = XExtentsOfFontSet(_fontset);
138     else
139       return False;
140
141     assert(_fontset_extents);
142   }
143     
144   fullname = buildXlfdName(False);
145   cerr << "loading font '" << fullname.c_str() << "'\n";
146   _font = XLoadQueryFont(_display, fullname.c_str());
147   if (! _font)
148     return False;
149   return True;
150 }
151
152
153 BFont::~BFont() {
154 #ifdef    XFT
155   if (_xftfont)
156     XftFontClose(_display, _xftfont);
157 #endif // XFT
158
159   if (i18n.multibyte() && _fontset)
160     XFreeFontSet(_display, _fontset);
161   if (_font)
162     XFreeFont(_display, _font);
163
164   if (_item)
165     _cache->release(_item);
166 }
167
168
169 /*
170  * Takes _name, _size, _bold, _italic, etc and builds them into a full XLFD.
171  */
172 string BFont::buildXlfdName(bool mb) const {
173   string weight = _bold ? "bold" : "medium";
174   string slant = _italic ? "i" : "r";
175   string sizestr= _size ? itostring(_size) : "*";
176
177   if (mb)
178     return _name + ',' +
179            "-*-*-" + weight + "-" + slant + "-*-*-" + sizestr +
180              "-*-*-*-*-*-*-*" + ',' +
181            "-*-*-*-*-*-*-" + sizestr + "-*-*-*-*-*-*-*" + ',' +
182            "*";
183   else if (_simplename)
184     return _name;
185   else
186     return "-*-" + _name + "-" + weight + "-" + slant + "-*-*-*-" +
187            sizestr + "-*-*-*-*-*-*";
188 }
189
190
191 /*
192  * Takes a full X font name and parses it out so we know if we're bold, our
193  * size, etc.
194  */
195 bool BFont::parseFontString(const string &xlfd) {
196   if (xlfd.empty() || xlfd[0] != '-') {
197     _name = xlfd;
198     _simplename = True;
199     _bold = False;
200     _italic = False;
201     _size = 0;
202   } else {
203     _simplename = False;
204     string weight,
205            slant,
206            sizestr;
207     int i = 0;
208
209     string::const_iterator it = xlfd.begin(), end = xlfd.end();
210     while(1) {
211       string::const_iterator tmp = it;   // current string.begin()
212       it = std::find(tmp, end, '-');     // look for comma between tmp and end
213       if (i == 2) _name = string(tmp, it); // s[tmp:it]
214       if (i == 3) weight = string(tmp, it);
215       if (i == 4) slant = string(tmp, it);
216       if (i == 8) sizestr = string(tmp, it);
217       if (it == end || i >= 8)
218         break;
219       ++it;
220       ++i;
221     }
222     if (i < 3)  // no name even! can't parse that
223       return False;
224     _bold = weight == "bold" || weight == "demibold";
225     _italic = slant == "i" || slant == "o";
226     if (atoi(sizestr.c_str()))
227       _size = atoi(sizestr.c_str());
228   }
229   
230   // min/max size restrictions for sanity, but 0 is the font's "default size"
231   if (_size && _size < 30)
232     _size = 30;
233   else if (_size > 970)
234     _size = 970;
235
236   return True;
237 }
238
239
240 void BFont::drawString(Drawable d, int x, int y, const BColor &color,
241                        const string &string) const {
242   assert(_valid);
243
244 #ifdef    XFT
245   if (_xftfont) {
246     XftDraw *draw = XftDrawCreate(_display, d, _screen->getVisual(),
247                                   _screen->getColormap());
248     assert(draw);
249
250     XftColor c;
251     c.color.red = color.red() | color.red() << 8;
252     c.color.green = color.green() | color.green() << 8;
253     c.color.blue = color.blue() | color.blue() << 8;
254     c.color.alpha = 0xff | 0xff << 8; // no transparency in BColor yet
255     c.pixel = color.pixel();
256     
257     XftDrawStringUtf8(draw, &c, _xftfont, x, _xftfont->ascent + y,
258                       (XftChar8 *) string.c_str(), string.size());
259
260     XftDrawDestroy(draw);
261     return;
262   }
263 #endif // XFT
264
265   if (! _cache)
266     _cache = color.display()->gcCache();
267   if (! _item)
268     _item = _cache->find(color, _font, GXcopy, ClipByChildren);
269
270   assert(_cache);
271   assert(_item);
272
273   if (i18n.multibyte())
274     XmbDrawString(_display, d, _fontset, _item->gc(),
275                   x, y - _fontset_extents->max_ink_extent.y,
276                   string.c_str(), string.size());
277   else
278     XDrawString(_display, d, _item->gc(),
279                 x, _font->ascent + y,
280                 string.c_str(), string.size());
281 }
282
283
284 unsigned int BFont::measureString(const string &string) const {
285   assert(_valid);
286
287 #ifdef    XFT
288   if (_xftfont) {
289     XGlyphInfo info;
290     XftTextExtentsUtf8(_display, _xftfont, (XftChar8 *) string.c_str(),
291                        string.size(), &info);
292     return info.xOff;
293   }
294 #endif // XFT
295
296   if (i18n.multibyte()) {
297     XRectangle ink, logical;
298     XmbTextExtents(_fontset, string.c_str(), string.size(), &ink, &logical);
299     return logical.width;
300   } else {
301     return XTextWidth(_font, string.c_str(), string.size());
302   }
303 }
304
305
306 unsigned int BFont::height(void) const {
307   assert(_valid);
308
309 #ifdef    XFT
310   if (_xftfont)
311     return _xftfont->height;
312 #endif // XFT
313
314   if (i18n.multibyte())
315     return _fontset_extents->max_ink_extent.height;
316   else
317     return _font->ascent + _font->descent;
318 }
319
320
321 unsigned int BFont::maxCharWidth(void) const {
322   assert(_valid);
323
324 #ifdef    XFT
325   if (_xftfont)
326     return _xftfont->max_advance_width;
327 #endif // XFT
328
329   if (i18n.multibyte())
330     return _fontset_extents->max_logical_extent.width;
331   else
332     return _font->max_bounds.rbearing - _font->min_bounds.lbearing;
333 }