X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ffrontends%2Fqt4%2FGuiPainter.cpp;h=afa2182eb671f4ee3e53208c0df13efc4a273e86;hb=425d092204118ea6c24c28e85fdf03fcf2bb51a4;hp=a3bc70cf7186281b18c5d08f3339eb709ba032a6;hpb=a1cec91afaca91968b46e695533c10ad2a3f73d3;p=lyx.git diff --git a/src/frontends/qt4/GuiPainter.cpp b/src/frontends/qt4/GuiPainter.cpp index a3bc70cf71..afa2182eb6 100644 --- a/src/frontends/qt4/GuiPainter.cpp +++ b/src/frontends/qt4/GuiPainter.cpp @@ -13,71 +13,57 @@ #include "GuiPainter.h" +#include "ColorCache.h" #include "GuiApplication.h" +#include "GuiFontLoader.h" #include "GuiFontMetrics.h" #include "GuiImage.h" - -#include "GuiApplication.h" #include "qt_helpers.h" -#include "debug.h" +#include "FontInfo.h" #include "Language.h" -#include "Color.h" +#include "LyXRC.h" -#include "support/unicode.h" +#include "insets/Inset.h" + +#include "support/lassert.h" +#include "support/debug.h" #include #include // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when // drawing text. This is especially useful for older PPC/Mac systems. -#if (QT_VERSION < 0x040200) || defined(Q_WS_X11) +#if defined(Q_WS_X11) #define USE_PIXMAP_CACHE 0 #else #define USE_PIXMAP_CACHE 1 #endif -using std::endl; -using std::string; +using namespace std; namespace lyx { namespace frontend { -namespace { - -bool const usePixmapCache = USE_PIXMAP_CACHE; - -QString generateStringSignature(QString const & str, Font const & f) -{ - QString sig = str; - sig.append(QChar(static_cast(f.family()))); - sig.append(QChar(static_cast(f.series()))); - sig.append(QChar(static_cast(f.realShape()))); - sig.append(QChar(static_cast(f.size()))); - sig.append(QChar(static_cast(f.color()))); - return sig; -} - -} // anon namespace - -QLPainter::QLPainter(QPaintDevice * device) - : QPainter(device), Painter() +GuiPainter::GuiPainter(QPaintDevice * device) + : QPainter(device), Painter(), + use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE) { // new QPainter has default QPen: - current_color_ = Color::black; + current_color_ = guiApp->colorCache().get(Color_black); current_ls_ = line_solid; current_lw_ = line_thin; } -QLPainter::~QLPainter() +GuiPainter::~GuiPainter() { QPainter::end(); - //lyxerr << "QLPainter::end()" << endl; + //lyxerr << "GuiPainter::end()" << endl; } -void QLPainter::setQPainterPen(Color_color col, +void GuiPainter::setQPainterPen(QColor const & col, Painter::line_style ls, Painter::line_width lw) { if (col == current_color_ && ls == current_ls_ && lw == current_lw_) @@ -88,8 +74,7 @@ void QLPainter::setQPainterPen(Color_color col, current_lw_ = lw; QPen pen = QPainter::pen(); - - pen.setColor(guiApp->colorCache().get(col)); + pen.setColor(col); switch (ls) { case line_solid: pen.setStyle(Qt::SolidLine); break; @@ -105,25 +90,96 @@ void QLPainter::setQPainterPen(Color_color col, } -void QLPainter::point(int x, int y, Color_color col) +QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f) +{ + QString sig = str; + sig.append(QChar(static_cast(f.family()))); + sig.append(QChar(static_cast(f.series()))); + sig.append(QChar(static_cast(f.realShape()))); + sig.append(QChar(static_cast(f.size()))); + sig.append(QChar(static_cast(f.color()))); + if (!monochrome_min_.empty()) { + QColor const & min = monochrome_min_.top(); + QColor const & max = monochrome_max_.top(); + sig.append(QChar(static_cast(min.red()))); + sig.append(QChar(static_cast(min.green()))); + sig.append(QChar(static_cast(min.blue()))); + sig.append(QChar(static_cast(max.red()))); + sig.append(QChar(static_cast(max.green()))); + sig.append(QChar(static_cast(max.blue()))); + } + return sig; +} + + +QColor GuiPainter::computeColor(Color col) +{ + return filterColor(guiApp->colorCache().get(col)); +} + + +QColor GuiPainter::filterColor(QColor const & col) +{ + if (monochrome_min_.empty()) + return col; + + // map into [min,max] interval + QColor const & min = monochrome_min_.top(); + QColor const & max = monochrome_max_.top(); + + qreal v = col.valueF(); + v *= v; // make it a bit steeper (i.e. darker) + + qreal minr, ming, minb; + qreal maxr, maxg, maxb; + min.getRgbF(&minr, &ming, &minb); + max.getRgbF(&maxr, &maxg, &maxb); + + QColor c; + c.setRgbF( + v * (minr - maxr) + maxr, + v * (ming - maxg) + maxg, + v * (minb - maxb) + maxb); + return c; +} + + +void GuiPainter::enterMonochromeMode(Color const & min, Color const & max) +{ + QColor qmin = filterColor(guiApp->colorCache().get(min)); + QColor qmax = filterColor(guiApp->colorCache().get(max)); + monochrome_min_.push(qmin); + monochrome_max_.push(qmax); +} + + +void GuiPainter::leaveMonochromeMode() +{ + LASSERT(!monochrome_min_.empty(), /**/); + monochrome_min_.pop(); + monochrome_max_.pop(); +} + + +void GuiPainter::point(int x, int y, Color col) { if (!isDrawingEnabled()) return; - setQPainterPen(col); + setQPainterPen(computeColor(col)); drawPoint(x, y); } -void QLPainter::line(int x1, int y1, int x2, int y2, - Color_color col, +void GuiPainter::line(int x1, int y1, int x2, int y2, + Color col, line_style ls, line_width lw) { if (!isDrawingEnabled()) return; - setQPainterPen(col, ls, lw); + setQPainterPen(computeColor(col), ls, lw); bool const do_antialiasing = renderHints() & TextAntialiasing && x1 != x2 && y1 != y2; setRenderHint(Antialiasing, do_antialiasing); @@ -132,8 +188,8 @@ void QLPainter::line(int x1, int y1, int x2, int y2, } -void QLPainter::lines(int const * xp, int const * yp, int np, - Color_color col, +void GuiPainter::lines(int const * xp, int const * yp, int np, + Color col, line_style ls, line_width lw) { @@ -152,7 +208,7 @@ void QLPainter::lines(int const * xp, int const * yp, int np, if (i != 0) antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i]; } - setQPainterPen(col, ls, lw); + setQPainterPen(computeColor(col), ls, lw); bool const text_is_antialiased = renderHints() & TextAntialiasing; setRenderHint(Antialiasing, antialias && text_is_antialiased); drawPolyline(points.data(), np); @@ -160,33 +216,36 @@ void QLPainter::lines(int const * xp, int const * yp, int np, } -void QLPainter::rectangle(int x, int y, int w, int h, - Color_color col, +void GuiPainter::rectangle(int x, int y, int w, int h, + Color col, line_style ls, line_width lw) { if (!isDrawingEnabled()) return; - setQPainterPen(col, ls, lw); + setQPainterPen(computeColor(col), ls, lw); drawRect(x, y, w, h); } -void QLPainter::fillRectangle(int x, int y, int w, int h, Color_color col) +void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col) { + if (!isDrawingEnabled()) + return; + fillRect(x, y, w, h, guiApp->colorCache().get(col)); } -void QLPainter::arc(int x, int y, unsigned int w, unsigned int h, - int a1, int a2, Color_color col) +void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h, + int a1, int a2, Color col) { if (!isDrawingEnabled()) return; // LyX usings 1/64ths degree, Qt usings 1/16th - setQPainterPen(col); + setQPainterPen(computeColor(col)); bool const do_antialiasing = renderHints() & TextAntialiasing; setRenderHint(Antialiasing, do_antialiasing); drawArc(x, y, w, h, a1 / 4, a2 / 4); @@ -194,37 +253,37 @@ void QLPainter::arc(int x, int y, unsigned int w, unsigned int h, } -void QLPainter::image(int x, int y, int w, int h, graphics::Image const & i) +void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i) { - graphics::QLImage const & qlimage = - static_cast(i); + graphics::GuiImage const & qlimage = + static_cast(i); - fillRectangle(x, y, w, h, Color::graphicsbg); + fillRectangle(x, y, w, h, Color_graphicsbg); if (!isDrawingEnabled()) return; - drawImage(x, y, qlimage.qimage(), 0, 0, w, h); + drawImage(x, y, qlimage.image(), 0, 0, w, h); } -int QLPainter::text(int x, int y, char_type c, Font const & f) +int GuiPainter::text(int x, int y, char_type c, FontInfo const & f) { docstring s(1, c); return text(x, y, s, f); } -int QLPainter::smallCapsText(int x, int y, - QString const & s, Font const & f) +int GuiPainter::smallCapsText(int x, int y, + QString const & s, FontInfo const & f) { - Font smallfont(f); - smallfont.decSize().decSize().setShape(Font::UP_SHAPE); + FontInfo smallfont(f); + smallfont.decSize().decSize().setShape(UP_SHAPE); - QFont const & qfont = guiApp->guiFontLoader().get(f); - QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont); + QFont const & qfont = getFont(f); + QFont const & qsmallfont = getFont(smallfont); - setQPainterPen(f.realColor()); + setQPainterPen(computeColor(f.realColor())); int textwidth = 0; size_t const ls = s.length(); for (unsigned int i = 0; i < ls; ++i) { @@ -242,9 +301,12 @@ int QLPainter::smallCapsText(int x, int y, } -int QLPainter::text(int x, int y, docstring const & s, - Font const & f) +int GuiPainter::text(int x, int y, docstring const & s, + FontInfo const & f) { + if (s.empty()) + return 0; + /* Caution: The following ucs4 to QString conversions work for symbol fonts only because they are no real conversions but simple casts in reality. When we want to draw a symbol or calculate the metrics we pass the position @@ -265,23 +327,37 @@ int QLPainter::text(int x, int y, docstring const & s, str = ' ' + str; #endif - QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f); + QFont const & ff = getFont(f); + GuiFontMetrics const & fm = getFontMetrics(f); int textwidth; - if (f.realShape() == Font::SMALLCAPS_SHAPE) { + if (f.realShape() == SMALLCAPS_SHAPE) { textwidth = smallCapsText(x, y, str, f); - if (f.underbar() == Font::ON) + if (f.underbar() == FONT_ON) underline(f, x, y, textwidth); + if (f.strikeout() == FONT_ON) + strikeoutLine(f, x, y, textwidth); + if (f.uuline() == FONT_ON) + doubleUnderline(f, x, y, textwidth); + if (f.uwave() == FONT_ON) + wavyHorizontalLine(x, y, textwidth, f.realColor().baseColor); return textwidth; } // Here we use the font width cache instead of // textwidth = fontMetrics().width(str); // because the above is awfully expensive on MacOSX - textwidth = fi.metrics->width(s); - if (f.underbar() == Font::ON) + textwidth = fm.width(s); + if (f.underbar() == FONT_ON) underline(f, x, y, textwidth); + if (f.strikeout() == FONT_ON) + strikeoutLine(f, x, y, textwidth); + if (f.uuline() == FONT_ON) + doubleUnderline(f, x, y, textwidth); + if (f.uwave() == FONT_ON) + // f.color() doesn't work on some circumstances + wavyHorizontalLine(x, y, textwidth, f.realColor().baseColor); if (!isDrawingEnabled()) return textwidth; @@ -291,9 +367,9 @@ int QLPainter::text(int x, int y, docstring const & s, // occurs at a line-break. As a kludge, we force Qt to // render this glyph using a one-column line. if (s.size() == 1 && str[0].unicode() == 0x00ad) { - setQPainterPen(f.realColor()); + setQPainterPen(computeColor(f.realColor())); QTextLayout adsymbol(str); - adsymbol.setFont(fi.font); + adsymbol.setFont(ff); adsymbol.beginLayout(); QTextLine line = adsymbol.createLine(); line.setNumColumns(1); @@ -303,60 +379,231 @@ int QLPainter::text(int x, int y, docstring const & s, return textwidth; } - if (!usePixmapCache) { - // don't use the pixmap cache, - // draw directly onto the painting device - setQPainterPen(f.realColor()); - if (font() != fi.font) - setFont(fi.font); - // We need to draw the text as LTR as we use our own bidi code. - setLayoutDirection(Qt::LeftToRight); - // We need to draw the text as LTR as we use our own bidi code. - setLayoutDirection(Qt::LeftToRight); - drawText(x, y, str); - LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8()) - << " at " << x << "," << y << std::endl; - return textwidth; - } + if (use_pixmap_cache_) { + QPixmap pm; + QString key = generateStringSignature(str, f); + + // Warning: Left bearing is in general negative! Only the case + // where left bearing is negative is of interest WRT the + // pixmap width and the text x-position. + // Only the left bearing of the first character is important + // as we always write from left to right, even for + // right-to-left languages. + int const lb = min(fm.lbearing(s[0]), 0); + int const mA = fm.maxAscent(); + if (QPixmapCache::find(key, pm)) { + // Draw the cached pixmap. + drawPixmap(x + lb, y - mA, pm); + return textwidth; + } - QPixmap pm; - QString key = generateStringSignature(str, f); - // Warning: Left bearing is in general negative! Only the case - // where left bearing is negative is of interest WRT the the - // pixmap width and the text x-position. - // Only the left bearing of the first character is important - // as we always write from left to right, even for - // right-to-left languages. - int const lb = std::min(fi.metrics->lbearing(s[0]), 0); - int const mA = fi.metrics->maxAscent(); - if (!QPixmapCache::find(key, pm)) { // Only the right bearing of the last character is // important as we always write from left to right, // even for right-to-left languages. - int const rb = fi.metrics->rbearing(s[s.size()-1]); + int const rb = fm.rbearing(s[s.size()-1]); int const w = textwidth + rb - lb; - int const mD = fi.metrics->maxDescent(); + int const mD = fm.maxDescent(); int const h = mA + mD; - pm = QPixmap(w, h); - pm.fill(Qt::transparent); - QLPainter p(&pm); - p.setQPainterPen(f.realColor()); - if (p.font() != fi.font) - p.setFont(fi.font); - // We need to draw the text as LTR as we use our own bidi code. - p.setLayoutDirection(Qt::LeftToRight); - p.drawText(-lb, mA, str); - QPixmapCache::insert(key, pm); - LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD - << " w=" << w << " lb=" << lb << " tw=" << textwidth - << " rb=" << rb << endl; + if (w > 0 && h > 0) { + pm = QPixmap(w, h); + pm.fill(Qt::transparent); + GuiPainter p(&pm); + p.setQPainterPen(computeColor(f.realColor())); + if (p.font() != ff) + p.setFont(ff); + // We need to draw the text as LTR as we use our own bidi code. + p.setLayoutDirection(Qt::LeftToRight); + p.drawText(-lb, mA, str); + QPixmapCache::insert(key, pm); + //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD + // << " w=" << w << " lb=" << lb << " tw=" << textwidth + // << " rb=" << rb); + + // Draw the new cached pixmap. + drawPixmap(x + lb, y - mA, pm); + + return textwidth; + } } - // Draw the cached pixmap. - drawPixmap(x + lb, y - mA, pm); + // don't use the pixmap cache, + // draw directly onto the painting device + setQPainterPen(computeColor(f.realColor())); + if (font() != ff) + setFont(ff); + // We need to draw the text as LTR as we use our own bidi code. + QPainter::setLayoutDirection(Qt::LeftToRight); + drawText(x, y, str); + //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8()) + // << " at " << x << "," << y); return textwidth; } +static int max(int a, int b) { return a > b ? a : b; } + + +void GuiPainter::button(int x, int y, int w, int h, bool mouseHover) +{ + if (mouseHover) + fillRectangle(x, y, w, h, Color_buttonhoverbg); + else + fillRectangle(x, y, w, h, Color_buttonbg); + buttonFrame(x, y, w, h); +} + + +void GuiPainter::buttonFrame(int x, int y, int w, int h) +{ + line(x, y, x, y + h - 1, Color_buttonframe); + line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe); + line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe); + line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe); +} + + +void GuiPainter::rectText(int x, int y, docstring const & str, + FontInfo const & font, Color back, Color frame) +{ + int width; + int ascent; + int descent; + + FontMetrics const & fm = theFontMetrics(font); + fm.rectText(str, width, ascent, descent); + + if (back != Color_none) + fillRectangle(x + 1, y - ascent + 1, width - 1, + ascent + descent - 1, back); + + if (frame != Color_none) + rectangle(x, y - ascent, width, ascent + descent, frame); + + text(x + 3, y, str, font); +} + + +void GuiPainter::buttonText(int x, int y, docstring const & str, + FontInfo const & font, bool mouseHover) +{ + int width; + int ascent; + int descent; + + FontMetrics const & fm = theFontMetrics(font); + fm.buttonText(str, width, ascent, descent); + + static int const d = Inset::TEXT_TO_INSET_OFFSET / 2; + + button(x + d, y - ascent, width - d, descent + ascent, mouseHover); + text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font); +} + + +int GuiPainter::preeditText(int x, int y, char_type c, + FontInfo const & font, preedit_style style) +{ + FontInfo temp_font = font; + FontMetrics const & fm = theFontMetrics(font); + int ascent = fm.maxAscent(); + int descent = fm.maxDescent(); + int height = ascent + descent; + int width = fm.width(c); + + switch (style) { + case preedit_default: + // default unselecting mode. + fillRectangle(x, y - height + 1, width, height, Color_background); + dashedUnderline(font, x, y - descent + 1, width); + break; + case preedit_selecting: + // We are in selecting mode: white text on black background. + fillRectangle(x, y - height + 1, width, height, Color_black); + temp_font.setColor(Color_white); + break; + case preedit_cursor: + // The character comes with a cursor. + fillRectangle(x, y - height + 1, width, height, Color_background); + underline(font, x, y - descent + 1, width); + break; + } + text(x, y - descent + 1, c, temp_font); + + return width; +} + + +void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width) +{ + FontMetrics const & fm = theFontMetrics(f); + + int const below = max(fm.maxDescent() / 2, 2); + + line(x, y + below, x + width, y + below, f.realColor()); + line(x, y + below - 2, x + width, y + below - 2, f.realColor()); +} + + +void GuiPainter::underline(FontInfo const & f, int x, int y, int width) +{ + FontMetrics const & fm = theFontMetrics(f); + + int const below = max(fm.maxDescent() / 2, 2); + int const height = max((fm.maxDescent() / 4) - 1, 1); + + if (height < 2) + line(x, y + below, x + width, y + below, f.realColor()); + else + fillRectangle(x, y + below, width, below + height, f.realColor()); +} + + +void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width) +{ + FontMetrics const & fm = theFontMetrics(f); + + int const middle = max((fm.maxHeight() / 4), 1); + int const height = middle/3; + + if (height < 2) + line(x, y - middle, x + width, y - middle, f.realColor()); + else + fillRectangle(x, y - middle, width, height, f.realColor()); +} + + +void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width) +{ + FontMetrics const & fm = theFontMetrics(f); + + int const below = max(fm.maxDescent() / 2, 2); + int height = max((fm.maxDescent() / 4) - 1, 1); + + if (height >= 2) + height += below; + + for (int n = 0; n != height; ++n) + line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash); +} + + +void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col) +{ + setQPainterPen(computeColor(col)); + int const step = 4; + int const xend = x + width; + int height = 1; + //FIXME: I am not sure if Antialiasing gives the best effect. + //setRenderHint(Antialiasing, true); + while (x < xend) { + height = - height; + drawLine(x, y - height, x + step, y + height); + x += step; + drawLine(x, y + height, x + 2, y + height); + x += 2; + } + //setRenderHint(Antialiasing, false); +} + } // namespace frontend } // namespace lyx