//
// PFont.cc for pekwm
// Copyright (C) 2003-2005 Claes Nasten <pekdon{@}pekdon{.}net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//
#include "../config.h"
#include "PFont.hh"
#include "PScreen.hh"
#include "Util.hh"
#include <iostream>
#include <cstring>
using std::cerr;
using std::endl;
using std::string;
using std::vector;
string PFont::_trim_string = string();
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)
{
}
//! @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 char *text, uint max_chars, uint max_width, PFont::TrimType trim_type)
{
if (text == NULL) {
return;
}
uint offset = x, chars = 0;
bool free_text = false;
char *real_text = (char*) text;
// if we have max width set, make sure our text isn't longer
if (max_width > 0) {
free_text = trim(&real_text, trim_type, max_width, chars);
offset += justify(real_text, max_width, 0,
(chars == 0) ? strlen(real_text) : chars);
// chars will be set in trim if calling that
} else if (chars == 0) {
chars = strlen(real_text);
}
// shadowed font
if ((_offset_x != 0) || (_offset_y != 0)) {
drawText(dest, offset + _offset_x, y + _ascent + _offset_y,
real_text, chars, false); // false as in bg
}
// fg text
drawText(dest, offset, y + _ascent, real_text, chars, true); // true as in fg
if (free_text) {
free(real_text);
}
}
//! @brief Trims the text making it max max_width wide
//! @return true if *text needs to be free, else false
bool
PFont::trim(char **text, PFont::TrimType trim_type, uint max_width, uint &chars)
{
bool free_text = false;
if (getWidth(*text) > max_width) {
if ((_trim_string.size() > 0) && (trim_type == FONT_TRIM_MIDDLE)) {
*text = trimMiddle(*text, max_width);
chars = trimEnd(*text, max_width);
free_text = true;
} else {
chars = trimEnd(*text, max_width);
}
} else {
chars = strlen(*text);
}
return free_text;
}
//! @brief Figures how many charachters to have before exceding max_width
uint
PFont::trimEnd(const char *text, uint max_width)
{
for (uint i = strlen(text); i > 0; --i) {
if (getWidth(text, i) <= max_width) {
return i;
}
}
return 0;
}
//! @brief Replace middle of string with _trim_string making it max_width wide
char*
PFont::trimMiddle(const char *text, uint max_width)
{
if (_trim_string.size() == 0) {
return strdup(text);
}
string dest;
uint chars = strlen(text), pos = 0;
// max width
uint max_side = (max_width / 2);
uint sep_width = getWidth(_trim_string.c_str(),
_trim_string.size() / 2 + _trim_string.size() % 2);
// if we can't hold the trim string, just do an trim end
if (max_side <= sep_width) {
return strdup(text);
}
// add space for the trim string
max_side -= sep_width;
// get numbers of chars before ...
for (uint i = chars; i > 0; --i) {
if (getWidth(text, i) < max_side) {
pos = i;
dest.insert(0, text, i);
break;
}
}
// get numbers of chars after ...
for (uint i = pos; i < chars; ++i) {
if (getWidth(text + i, chars - i) < max_side) {
dest.insert(dest.size(), text + i, chars - i);
break;
}
}
// got a char after and before
if (dest.size() > 1) {
if ((getWidth(dest.c_str()) + getWidth(_trim_string.c_str())) < max_width) {
dest.insert(pos, _trim_string);
}
// not enough room to place a characther in max_side, do trim end instead
} else {
return strdup(text);
}
#ifdef DEBUG
if (getWidth(dest.c_str()) > max_width) {
cerr << __FILE__ << "@" << __LINE__ << ": "
<< "width(" << getWidth(dest.c_str()) << ") is greater than max_width("
<< max_width << ")" << endl;
}
#endif // DEBUG
return strdup(dest.c_str());
}
//! @brief Justifies the string based on _justify property of the Font
uint
PFont::justify(const char *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(NULL),
_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 == NULL) {
_font = XLoadQueryFont(_scr->getDpy(), FALLBACK_FONT);
if (_font == NULL) {
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 = NULL;
}
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 char *text, uint max_chars)
{
if (text == NULL)
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 == 0) || (max_chars > strlen(text)))
max_chars = strlen(text);
uint width = 0;
if (_font != NULL) {
width = XTextWidth(_font, text, max_chars);
}
return (width + _offset_x);
}
//! @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);
}
}
//! @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 char *text, uint chars, bool fg)
{
GC gc = fg ? _gc_fg : _gc_bg;
if (_font && (gc != None)) {
XDrawString(_scr->getDpy(), dest, gc, x, y, text, chars);
}
}
// PFontXmb
//! @brief PFontXmb constructor
PFontXmb::PFontXmb(PScreen *scr) : PFont(scr),
_fontset(NULL),
_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();
char *def_string;
char **missing_list, **font_names;
int missing_count, fnum;
XFontStruct **fonts;
string basename(font_name);
basename.append(",*");
_fontset =
XCreateFontSet(_scr->getDpy(), basename.c_str(),
&missing_list, &missing_count, &def_string);
if (_fontset == NULL) {
return false;
}
for (int i = 0; i < missing_count; ++i)
cerr << "PFontXmb::load(): Missing charset" << missing_list[i] << endl;
_ascent = _descent = 0;
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;
// create GC's
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);
return true;
}
//! @brief Unloads font resources
void
PFontXmb::unload(void)
{
if (_fontset != NULL) {
XFreeFontSet(_scr->getDpy(), _fontset);
_fontset = NULL;
}
}
//! @brief Gets the width the text would take using this font
uint
PFontXmb::getWidth(const char *text, uint max_chars)
{
if (text == NULL)
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 == 0) || (max_chars > strlen(text)))
max_chars = strlen(text);
uint width = 0;
if (_fontset != NULL) {
XRectangle ink, logical;
XmbTextExtents(_fontset, text, 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 char *text, uint chars, bool fg)
{
GC gc = fg ? _gc_fg : _gc_bg;
if ((_fontset != NULL) && (gc != None)) {
XmbDrawString(_scr->getDpy(), dest, _fontset, gc, x, y, text, chars);
}
}
//! @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(NULL), _font(NULL),
_cl_fg(NULL), _cl_bg(NULL)
{
_type = FONT_TYPE_XFT;
_draw = XftDrawCreate(_scr->getDpy(), _scr->getRoot(),
_scr->getVisual()->getXVisual(), _scr->getColormap());
}
//! @brief PFontXft destructor
PFontXft::~PFontXft(void)
{
XftDrawDestroy(_draw);
if (_cl_fg != NULL) {
XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(),
_scr->getColormap(), _cl_fg);
}
if (_cl_bg != NULL) {
XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(),
_scr->getColormap(), _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 == NULL) {
_font = XftFontOpenXlfd(_scr->getDpy(), _scr->getScreenNum(),
font_name.c_str());
}
if (_font != NULL) {
_ascent = _font->ascent;
_descent = _font->descent;
_height = _font->height;
return true;
}
return false;
}
//! @brief Unloads font resources
void
PFontXft::unload(void)
{
if (_font != NULL) {
XftFontClose(_scr->getDpy(), _font);
_font = NULL;
}
}
//! @brief Gets the width the text would take using this font
uint
PFontXft::getWidth(const char *text, uint max_chars)
{
uint width = 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 > strlen(text)) || (max_chars < 1))
max_chars = strlen(text);
if (_font != NULL) {
XGlyphInfo extents;
XftTextExtents8(_scr->getDpy(), _font,
(XftChar8 *) text, max_chars, &extents);
width = extents.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
PFontXft::drawText(Drawable dest, int x, int y, const char *text, uint chars, bool fg)
{
XftColor *cl = fg ? _cl_fg : _cl_bg;
if ((_font != NULL) && (cl != NULL)) {
XftDrawChange(_draw, dest);
XftDrawString8(_draw, cl, _font, x, y, (XftChar8 *) text, chars);
}
}
//! @brief Sets the color that should be used when drawing
void
PFontXft::setColor(PFont::Color *color)
{
if (_cl_fg != NULL) {
XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(),
_scr->getColormap(), _cl_fg);
_cl_fg = NULL;
}
if (_cl_bg != NULL) {
XftColorFree(_scr->getDpy(), _scr->getVisual()->getXVisual(),
_scr->getColormap(), _cl_bg);
_cl_bg = NULL;
}
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
syntax highlighted by Code2HTML, v. 0.9.1