// // PFont.cc for pekwm // Copyright © 2003-2009 Nästén // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include #include #include "PFont.hh" #include "PScreen.hh" #include "Util.hh" using std::cerr; using std::endl; using std::string; using std::vector; using std::wstring; wstring PFont::_trim_string = wstring(); const char *FALLBACK_FONT = "fixed"; // PFont //! @brief PFont constructor PFont::PFont(PScreen *scr) : _scr(scr), _height(0), _ascent(0), _descent(0), _offset_x(0), _offset_y(0), _justify(FONT_JUSTIFY_LEFT) { _type = FONT_TYPE_NO; } //! @brief PFont destructor PFont::~PFont(void) { unload(); } //! @brief Draws the text on the drawable. //! @param dest destination Drawable //! @param x x start position //! @param y y start position //! @param text text to draw //! @param max_chars max nr of chars, defaults to 0 == strlen(text) //! @param max_width max nr of pixels, defaults to 0 == infinity //! @param trim_type how to trim title if not enough space, defaults to FONT_TRIM_END void PFont::draw(Drawable dest, int x, int y, const std::wstring &text, uint max_chars, uint max_width, PFont::TrimType trim_type) { if (! text.size()) { return; } uint offset = x, chars = max_chars; wstring real_text(text); if (max_width > 0) { // If max width set, make sure text fits in max_width pixels. trim(real_text, trim_type, max_width); offset += justify(real_text, max_width, 0, (chars == 0) ? real_text.size() : chars); } else if (chars == 0) { // Just set to complete string. chars = real_text.size(); } // Draw shadowed font if x or y offset is specified if (_offset_x || _offset_y) { drawText(dest, offset + _offset_x, y + _ascent + _offset_y, real_text, chars, false); // false as in bg } // Draw main font drawText(dest, offset, y + _ascent, real_text, chars, true); // true as in fg } //! @brief Trims the text making it max max_width wide //! @param text //! @param trim_type //! @param max_width //! @param chars void PFont::trim(std::wstring &text, PFont::TrimType trim_type, uint max_width) { if (getWidth(text) > max_width) { if ((_trim_string.size() > 0) && (trim_type == FONT_TRIM_MIDDLE)) { // Trim middle before end if trim string is set. trimMiddle(text, max_width); } trimEnd(text, max_width); } } //! @brief Figures how many charachters to have before exceding max_width void PFont::trimEnd(std::wstring &text, uint max_width) { for (uint i = text.size(); i > 0; --i) { if (getWidth(text, i) <= max_width) { text = text.substr(0, i); break; } } } //! @brief Replace middle of string with _trim_string making it max_width wide void PFont::trimMiddle(std::wstring &text, uint max_width) { // Get max and separator width uint max_side = (max_width / 2); uint sep_width = getWidth(_trim_string.c_str(), _trim_string.size() / 2 + _trim_string.size() % 2); uint pos = 0; wstring dest; // If the trim string is too large, do nothing and let trimEnd handle this. if (max_side <= sep_width) { return; } // Add space for the trim string max_side -= sep_width; // Get numbers of chars before trim string (..) for (uint i = text.size(); i > 0; --i) { if (getWidth(text, i) < max_side) { pos = i; dest.insert(0, text.substr(0, i)); break; } } // get numbers of chars after ... if (pos < text.size()) { for (uint i = pos; i < text.size(); ++i) { wstring second_part(text.substr(i, text.size() - i)); if (getWidth(second_part, 0) < max_side) { dest.insert(dest.size(), second_part); break; } } // Got a char after and before, if not do nothing and trimEnd will handle // trimming after this call. if (dest.size() > 1) { if ((getWidth(dest) + getWidth(_trim_string)) < max_width) { dest.insert(pos, _trim_string); } // Update original string text = dest; } } } //! @brief Justifies the string based on _justify property of the Font uint PFont::justify(const std::wstring &text, uint max_width, uint padding, uint chars) { uint x; uint width = getWidth(text, chars); switch(_justify) { case FONT_JUSTIFY_CENTER: x = (max_width - width) / 2; break; case FONT_JUSTIFY_RIGHT: x = max_width - width - padding; break; default: x = padding; break; } return x; } // PFontX11 //! @brief PFontX11 constructor PFontX11::PFontX11(PScreen *scr) : PFont(scr), _font(0), _gc_fg(None), _gc_bg(None) { _type = FONT_TYPE_X11; } //! @brief PFontX11 destructor PFontX11::~PFontX11(void) { unload(); } //! @brief Loads the X11 font font_name bool PFontX11::load(const std::string &font_name) { unload(); _font = XLoadQueryFont(_scr->getDpy(), font_name.c_str()); if (! _font) { _font = XLoadQueryFont(_scr->getDpy(), FALLBACK_FONT); if (! _font) { return false; } } _height = _font->ascent + _font->descent; _ascent = _font->ascent; _descent = _font->descent; // create GC's XGCValues gv; uint mask = GCFunction|GCFont; gv.function = GXcopy; gv.font = _font->fid; _gc_fg = XCreateGC(_scr->getDpy(), _scr->getRoot(), mask, &gv); _gc_bg = XCreateGC(_scr->getDpy(), _scr->getRoot(), mask, &gv); return true; } //! @brief Unloads font resources void PFontX11::unload(void) { if (_font) { XFreeFont(_scr->getDpy(), _font); _font = 0; } if (_gc_fg != None) { XFreeGC(_scr->getDpy(), _gc_fg); _gc_fg = None; } if (_gc_bg != None) { XFreeGC(_scr->getDpy(), _gc_bg); _gc_bg = None; } } //! @brief Gets the width the text would take using this font uint PFontX11::getWidth(const wstring &text, uint max_chars) { if (! text.size()) { return 0; } // Make sure the max_chars has a decent value, if it's less than 1 wich it // defaults to set it to the numbers of chars in the string text if (! max_chars || (max_chars > text.size())) { max_chars = text.size(); } uint width = 0; if (_font) { // No UTF8 support, convert to locale encoding. string mb_text(Util::to_mb_str(text.substr(0, max_chars))); width = XTextWidth(_font, mb_text.c_str(), mb_text.size()); } return (width + _offset_x); } //! @brief Draws the text on dest //! @param dest Drawable to draw on //! @param chars Max numbers of characthers to print //! @param fg Bool, to use foreground or background colors void PFontX11::drawText(Drawable dest, int x, int y, const std::wstring &text, uint chars, bool fg) { GC gc = fg ? _gc_fg : _gc_bg; if (_font && (gc != None)) { string mb_text; if (chars != 0) { mb_text = Util::to_mb_str(text.substr(0, chars)); } else { mb_text = Util::to_mb_str(text); } XDrawString(_scr->getDpy(), dest, gc, x, y, mb_text.c_str(), mb_text.size()); } } //! @brief Sets the color that should be used when drawing void PFontX11::setColor(PFont::Color *color) { if (color->hasFg()) { XSetForeground(_scr->getDpy(), _gc_fg, color->getFg()->pixel); } if (color->hasBg()) { XSetForeground(_scr->getDpy(), _gc_bg, color->getBg()->pixel); } } // PFontXmb const char* PFontXmb::DEFAULT_FONTSET = "fixed*"; //! @brief PFontXmb constructor PFontXmb::PFontXmb(PScreen *scr) : PFont(scr), _fontset(0), _gc_fg(None), _gc_bg(None) { _type = FONT_TYPE_XMB; } //! @brief PFontXmb destructor PFontXmb::~PFontXmb(void) { unload(); } //! @brief Loads Xmb font font_name bool PFontXmb::load(const std::string &font_name) { unload(); string basename(font_name); if (font_name.rfind(",*") != (font_name.size() - 2)) { basename.append(",*"); } // Create GC XGCValues gv; uint mask = GCFunction; gv.function = GXcopy; _gc_fg = XCreateGC(_scr->getDpy(), _scr->getRoot(), mask, &gv); _gc_bg = XCreateGC(_scr->getDpy(), _scr->getRoot(), mask, &gv); // Try to load font char *def_string; char **missing_list, **font_names; int missing_count; _fontset = XCreateFontSet(_scr->getDpy(), basename.c_str(), &missing_list, &missing_count, &def_string); if (! _fontset) { cerr << "PFontXmb::load(): Failed to create fontset for " << font_name << endl; _fontset = XCreateFontSet(_scr->getDpy(), DEFAULT_FONTSET, &missing_list, &missing_count, &def_string); } if (_fontset) { for (int i = 0; i < missing_count; ++i) { cerr << "PFontXmb::load(): Missing charset" << missing_list[i] << endl; } _ascent = _descent = 0; XFontStruct **fonts; int fnum = XFontsOfFontSet (_fontset, &fonts, &font_names); for (int i = 0; i < fnum; ++i) { if (signed(_ascent) < fonts[i]->ascent) { _ascent = fonts[i]->ascent; } if (signed(_descent) < fonts[i]->descent) { _descent = fonts[i]->descent; } } _height = _ascent + _descent; } else { return false; } return true; } //! @brief Unloads font resources void PFontXmb::unload(void) { if (_fontset) { XFreeFontSet(_scr->getDpy(), _fontset); _fontset = 0; } } //! @brief Gets the width the text would take using this font uint PFontXmb::getWidth(const std::wstring &text, uint max_chars) { if (! text.size()) { return 0; } // Make sure the max_chars has a decent value, if it's less than 1 wich it // defaults to set it to the numbers of chars in the string text if (! max_chars || (max_chars > text.size())) { max_chars = text.size(); } uint width = 0; if (_fontset) { XRectangle ink, logical; XwcTextExtents(_fontset, text.c_str(), max_chars, &ink, &logical); width = logical.width; } return (width + _offset_x); } //! @brief Draws the text on dest //! @param dest Drawable to draw on //! @param chars Max numbers of characthers to print //! @param fg Bool, to use foreground or background colors void PFontXmb::drawText(Drawable dest, int x, int y, const std::wstring &text, uint chars, bool fg) { GC gc = fg ? _gc_fg : _gc_bg; if (_fontset && (gc != None)) { XwcDrawString(_scr->getDpy(), dest, _fontset, gc, x, y, text.c_str(), chars ? chars : text.size()); } } //! @brief Sets the color that should be used when drawing void PFontXmb::setColor(PFont::Color *color) { if (color->hasFg()) { XSetForeground(_scr->getDpy(), _gc_fg, color->getFg()->pixel); } if (color->hasBg()) { XSetForeground(_scr->getDpy(), _gc_bg, color->getBg()->pixel); } } // PFontXft #ifdef HAVE_XFT //! @brief PFontXft constructor PFontXft::PFontXft(PScreen *scr) : PFont(scr), _draw(0), _font(0), _cl_fg(0), _cl_bg(0) { _type = FONT_TYPE_XFT; _draw = XftDrawCreate(_scr->getDpy(), _scr->getRoot(), _scr->getVisual()->getXVisual(), _scr->getColormap()); } //! @brief PFontXft destructor PFontXft::~PFontXft(void) { unload(); XftDrawDestroy(_draw); if (_cl_fg) { XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), _cl_fg); delete _cl_fg; } if (_cl_bg) { XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), _cl_bg); delete _cl_bg; } } //! @brief Loads the Xft font font_name bool PFontXft::load(const std::string &font_name) { unload(); _font = XftFontOpenName(_scr->getDpy(), _scr->getScreenNum(), font_name.c_str()); if (! _font) { _font = XftFontOpenXlfd(_scr->getDpy(), _scr->getScreenNum(), font_name.c_str()); } if (_font) { _ascent = _font->ascent; _descent = _font->descent; _height = _font->height; return true; } return false; } //! @brief Unloads font resources void PFontXft::unload(void) { if (_font) { XftFontClose(_scr->getDpy(), _font); _font = 0; } } //! @brief Gets the width the text would take using this font uint PFontXft::getWidth(const std::wstring &text, uint max_chars) { if (! text.size()) { return 0; } // Make sure the max_chars has a decent value, if it's less than 1 wich it // defaults to set it to the numbers of chars in the string text if (! max_chars || (max_chars > text.size())) { max_chars = text.size(); } uint width = 0; if (_font) { string utf8_text(Util::to_utf8_str(text.substr(0, max_chars))); XGlyphInfo extents; XftTextExtentsUtf8(_scr->getDpy(), _font, reinterpret_cast(utf8_text.c_str()), utf8_text.size(), &extents); width = extents.xOff; } return (width + _offset_x); } //! @brief Draws the text on dest //! @param dest Drawable to draw on //! @param chars Max numbers of characthers to print //! @param fg Bool, to use foreground or background colors void PFontXft::drawText(Drawable dest, int x, int y, const std::wstring &text, uint chars, bool fg) { XftColor *cl = fg ? _cl_fg : _cl_bg; if (_font && cl) { string utf8_text; if (chars != 0) { utf8_text = Util::to_utf8_str(text.substr(0, chars)); } else { utf8_text = Util::to_utf8_str(text); } XftDrawChange(_draw, dest); XftDrawStringUtf8(_draw, cl, _font, x, y, reinterpret_cast(utf8_text.c_str()), utf8_text.size()); } } //! @brief Sets the color that should be used when drawing void PFontXft::setColor(PFont::Color *color) { if (_cl_fg) { XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), _cl_fg); delete _cl_fg; _cl_fg = 0; } if (_cl_bg) { XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), _cl_bg); delete _cl_bg; _cl_bg = 0; } if (color->hasFg()) { _xrender_color.red = color->getFg()->red; _xrender_color.green = color->getFg()->green; _xrender_color.blue = color->getFg()->blue; _xrender_color.alpha = color->getFgAlpha(); _cl_fg = new XftColor; XftColorAllocValue(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), &_xrender_color, _cl_fg); } if (color->hasBg()) { _xrender_color.red = color->getBg()->red; _xrender_color.green = color->getBg()->green; _xrender_color.blue = color->getBg()->blue; _xrender_color.alpha = color->getBgAlpha(); _cl_bg = new XftColor; XftColorAllocValue(_scr->getDpy(), _scr->getVisual()->getXVisual(), _scr->getColormap(), &_xrender_color, _cl_bg); } } #endif // HAVE_XFT