]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiPainter.cpp
Correct early return position for if use_pixmap_cache_ check
[lyx.git] / src / frontends / qt4 / GuiPainter.cpp
index 68161d1be9edd4e561eb9dd1f2b6ba7dc20180ca..b2ccbfc41c60664f683635159eec21f8e7519d3b 100644 (file)
@@ -20,7 +20,7 @@
 #include "GuiImage.h"
 #include "qt_helpers.h"
 
-#include "FontInfo.h"
+#include "Font.h"
 #include "Language.h"
 #include "LyXRC.h"
 
 
 // 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 defined(Q_WS_X11)
+#if defined(Q_WS_X11) || defined(QPA_XCB)
 #define USE_PIXMAP_CACHE 0
 #else
 #define USE_PIXMAP_CACHE 1
 #endif
 
 using namespace std;
+using namespace lyx::support;
 
 namespace lyx {
 namespace frontend {
 
-GuiPainter::GuiPainter(QPaintDevice * device)
-       : QPainter(device), Painter(),
+const int Painter::thin_line = 0;
+
+GuiPainter::GuiPainter(QPaintDevice * device, double pixel_ratio)
+       : QPainter(device), Painter(pixel_ratio),
          use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
 {
        // new QPainter has default QPen:
        current_color_ = guiApp->colorCache().get(Color_black);
        current_ls_ = line_solid;
-       current_lw_ = line_thin;
+       current_lw_ = thin_line;
 }
 
 
@@ -64,7 +67,7 @@ GuiPainter::~GuiPainter()
 
 
 void GuiPainter::setQPainterPen(QColor const & col,
-       Painter::line_style ls, Painter::line_width lw)
+       Painter::line_style ls, int lw)
 {
        if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
                return;
@@ -81,10 +84,7 @@ void GuiPainter::setQPainterPen(QColor const & col,
                case line_onoffdash: pen.setStyle(Qt::DotLine); break;
        }
 
-       switch (lw) {
-               case line_thin: pen.setWidth(0); break;
-               case line_thick: pen.setWidth(3); break;
-       }
+       pen.setWidth(lw);
 
        setPen(pen);
 }
@@ -97,7 +97,9 @@ QString GuiPainter::generateStringSignature(QString const & str, FontInfo const
        sig.append(QChar(static_cast<short>(f.series())));
        sig.append(QChar(static_cast<short>(f.realShape())));
        sig.append(QChar(static_cast<short>(f.size())));
-       sig.append(QChar(static_cast<short>(f.color())));
+       Color const & color = f.realColor();
+       sig.append(QChar(static_cast<short>(color.baseColor)));
+       sig.append(QChar(static_cast<short>(color.mergeColor)));
        if (!monochrome_min_.empty()) {
                QColor const & min = monochrome_min_.top();
                QColor const & max = monochrome_max_.top();
@@ -112,7 +114,7 @@ QString GuiPainter::generateStringSignature(QString const & str, FontInfo const
 }
 
 
-QColor GuiPainter::computeColor(ColorCode col)
+QColor GuiPainter::computeColor(Color col)
 {
        return filterColor(guiApp->colorCache().get(col));
 }
@@ -144,7 +146,7 @@ QColor GuiPainter::filterColor(QColor const & col)
 }
 
 
-void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
+void GuiPainter::enterMonochromeMode(Color const & min, Color const & max)
 {
        QColor qmin = filterColor(guiApp->colorCache().get(min));
        QColor qmax = filterColor(guiApp->colorCache().get(max));
@@ -155,13 +157,13 @@ void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & ma
 
 void GuiPainter::leaveMonochromeMode()
 {
-       LASSERT(!monochrome_min_.empty(), /**/);
+       LASSERT(!monochrome_min_.empty(), return);
        monochrome_min_.pop();
        monochrome_max_.pop();
 }
 
 
-void GuiPainter::point(int x, int y, ColorCode col)
+void GuiPainter::point(int x, int y, Color col)
 {
        if (!isDrawingEnabled())
                return;
@@ -172,9 +174,9 @@ void GuiPainter::point(int x, int y, ColorCode col)
 
 
 void GuiPainter::line(int x1, int y1, int x2, int y2,
-       ColorCode col,
+       Color col,
        line_style ls,
-       line_width lw)
+       int lw)
 {
        if (!isDrawingEnabled())
                return;
@@ -189,14 +191,16 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
 
 
 void GuiPainter::lines(int const * xp, int const * yp, int np,
-       ColorCode col,
+       Color col,
+       fill_style fs,
        line_style ls,
-       line_width lw)
+       int lw)
 {
        if (!isDrawingEnabled())
                return;
 
        // double the size if needed
+       // FIXME THREAD
        static QVector<QPoint> points(32);
        if (np > points.size())
                points.resize(2 * np);
@@ -208,18 +212,27 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
                if (i != 0)
                        antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
        }
-       setQPainterPen(computeColor(col), ls, lw);
+       QColor const color = computeColor(col);
+       setQPainterPen(color, ls, lw);
        bool const text_is_antialiased = renderHints() & TextAntialiasing;
        setRenderHint(Antialiasing, antialias && text_is_antialiased);
-       drawPolyline(points.data(), np);
+       if (fs == fill_none) {
+               drawPolyline(points.data(), np);
+       } else {
+               QBrush const oldbrush = brush();
+               setBrush(QBrush(color));
+               drawPolygon(points.data(), np, fs == fill_oddeven ?
+                           Qt::OddEvenFill : Qt::WindingFill);
+               setBrush(oldbrush);
+       }
        setRenderHint(Antialiasing, false);
 }
 
 
 void GuiPainter::rectangle(int x, int y, int w, int h,
-       ColorCode col,
+       Color col,
        line_style ls,
-       line_width lw)
+       int lw)
 {
        if (!isDrawingEnabled())
                return;
@@ -229,7 +242,7 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
 }
 
 
-void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
+void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
 {
        if (!isDrawingEnabled())
                return;
@@ -239,7 +252,7 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
 
 
 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
-       int a1, int a2, ColorCode col)
+       int a1, int a2, Color col)
 {
        if (!isDrawingEnabled())
                return;
@@ -263,47 +276,24 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
        if (!isDrawingEnabled())
                return;
 
-       drawImage(x, y, qlimage.image(), 0, 0, w, h);
+       QImage const image = qlimage.image();
+       QRectF const drect = QRectF(x, y, w, h);
+       QRectF const srect = QRectF(0, 0, image.width(), image.height());
+       drawImage(drect, image, srect);
 }
 
 
 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
 {
-       docstring s(1, c);
-       return text(x, y, s, f);
-}
-
-
-int GuiPainter::smallCapsText(int x, int y,
-       QString const & s, FontInfo const & f)
-{
-       FontInfo smallfont(f);
-       smallfont.decSize().decSize().setShape(UP_SHAPE);
-
-       QFont const & qfont = getFont(f);
-       QFont const & qsmallfont = getFont(smallfont);
-
-       setQPainterPen(computeColor(f.realColor()));
-       int textwidth = 0;
-       size_t const ls = s.length();
-       for (unsigned int i = 0; i < ls; ++i) {
-               QChar const c = s[i].toUpper();
-               if (c != s.at(i)) {
-                       setFont(qsmallfont);
-               } else {
-                       setFont(qfont);
-               }
-               if (isDrawingEnabled())
-                       drawText(x + textwidth, y, c);
-               textwidth += fontMetrics().width(c);
-       }
-       return textwidth;
+       return text(x, y, docstring(1, c), f);
 }
 
 
 int GuiPainter::text(int x, int y, docstring const & s,
-               FontInfo const & f)
+                     FontInfo const & f, bool const rtl,
+                     double const wordspacing)
 {
+       //LYXERR0("text: x=" << x << ", s=" << s);
        if (s.empty())
                return 0;
 
@@ -327,70 +317,38 @@ int GuiPainter::text(int x, int y, docstring const & s,
                str = ' ' + str;
 #endif
 
-       QFont const & ff = getFont(f); 
-       GuiFontMetrics const & fm = getFontMetrics(f); 
-
-       int textwidth;
-
-       if (f.realShape() == SMALLCAPS_SHAPE) {
-               textwidth = smallCapsText(x, y, str, f);
-               if (f.underbar() == FONT_ON)
-                       underline(f, x, y, textwidth);
-               return textwidth;
-       }
+       QFont ff = getFont(f);
+       ff.setWordSpacing(wordspacing);
+       GuiFontMetrics const & fm = getFontMetrics(f);
 
        // Here we use the font width cache instead of
        //   textwidth = fontMetrics().width(str);
        // because the above is awfully expensive on MacOSX
-       textwidth = fm.width(s);
-       if (f.underbar() == FONT_ON)
-               underline(f, x, y, textwidth);
+       int const textwidth = fm.width(s);
 
        if (!isDrawingEnabled())
                return textwidth;
 
-       // Qt4 does not display a glyph whose codepoint is the
-       // same as that of a soft-hyphen (0x00ad), unless it
-       // 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(computeColor(f.realColor()));
-               QTextLayout adsymbol(str);
-               adsymbol.setFont(ff);
-               adsymbol.beginLayout();
-               QTextLine line = adsymbol.createLine();
-               line.setNumColumns(1);
-               line.setPosition(QPointF(0, -line.ascent()));
-               adsymbol.endLayout();
-               line.draw(this, QPointF(x, y));
-               return textwidth;
-       }
-
-       if (!use_pixmap_cache_) {
-               // 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;
-       }
+       textDecoration(f, x, y, 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 = min(fm.lbearing(s[0]), 0);
-       int const mA = fm.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.
@@ -398,32 +356,126 @@ int GuiPainter::text(int x, int y, docstring const & s,
                int const w = textwidth + rb - lb;
                int const mD = fm.maxDescent();
                int const h = mA + mD;
-               if (w <= 0 || h <= 0) {
-                       LYXERR(Debug::PAINTING, "Invalid pixmap cache image for '" << s << "' h=" << h << " w=" << w);
-                       return textwidth;
+               if (w > 0 && h > 0) {
+                       pm = QPixmap(static_cast<int>(pixelRatio() * w),
+                                                static_cast<int>(pixelRatio() * h));
+#if QT_VERSION >= 0x050000
+                       pm.setDevicePixelRatio(pixelRatio());
+#endif
+                       pm.fill(Qt::transparent);
+                       GuiPainter p(&pm, pixelRatio());
+                       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);
                }
-
-               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);
+               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);
+
+        /* In LyX, the character direction is forced by the language.
+         * Therefore, we have to signal that fact to Qt.
+         */
+#if 1
+       /* Use unicode override characters to enforce drawing direction
+        * Source: http://www.iamcal.com/understanding-bidirectional-text/
+        */
+       if (rtl)
+               // Right-to-left override: forces to draw text right-to-left
+               str = QChar(0x202E) + str;
+       else
+               // Left-to-right override: forces to draw text left-to-right
+               str =  QChar(0x202D) + str;
+       drawText(x, y, str);
+#else
+       /* This looks like a cleaner solution, but it has drawbacks
+        * - does not work reliably (Mac OS X, ...)
+        * - it is not really documented
+        * Keep it here for now, in case it can be helpful
+        */
+       //This is much stronger than setLayoutDirection.
+       int flag = rtl ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight;
+       drawText(x + (rtl ? textwidth : 0), y - fm.maxAscent(), 0, 0,
+                flag | Qt::TextDontClip,
+                str);
+#endif
+       //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
+       //      << " at " << x << "," << y);
+       return textwidth;
+}
+
+
+int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
+                     double const wordspacing)
+{
+       return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft(), wordspacing);
+}
+
+
+int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
+                     Color other, size_type const from, size_type const to,
+                     double const wordspacing)
+{
+       GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
+       FontInfo fi = f.fontInfo();
+       bool const rtl = f.isVisibleRightToLeft();
+
+       // dimensions
+       int const ascent = fm.maxAscent();
+       int const height = fm.maxAscent() + fm.maxDescent();
+       int xmin = fm.pos2x(str, from, rtl, wordspacing);
+       int xmax = fm.pos2x(str, to, rtl, wordspacing);
+       if (xmin > xmax)
+               swap(xmin, xmax);
+
+       // First the part in other color
+       Color const orig = fi.realColor();
+       fi.setPaintColor(other);
+       QRegion const clip(x + xmin, y - ascent, xmax - xmin, height);
+       setClipRegion(clip);
+       int const textwidth = text(x, y, str, fi, rtl, wordspacing);
+
+       // Then the part in normal color
+       // Note that in Qt5, it is not possible to use Qt::UniteClip,
+       // therefore QRegion is used.
+       fi.setPaintColor(orig);
+       QRegion region(viewport());
+       setClipRegion(region - clip);
+       text(x, y, str, fi, rtl, wordspacing);
+       setClipping(false);
 
        return textwidth;
 }
 
 
+void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
+{
+       if (f.underbar() == FONT_ON)
+               underline(f, x, y, width);
+       if (f.strikeout() == FONT_ON)
+               strikeoutLine(f, x, y, width);
+       if (f.uuline() == FONT_ON)
+               doubleUnderline(f, x, y, width);
+       if (f.uwave() == FONT_ON)
+               // f.color() doesn't work on some circumstances
+               wavyHorizontalLine(x, y, width,  f.realColor().baseColor);
+}
+
+
 static int max(int a, int b) { return a > b ? a : b; }
 
 
@@ -447,7 +499,7 @@ void GuiPainter::buttonFrame(int x, int y, int w, int h)
 
 
 void GuiPainter::rectText(int x, int y, docstring const & str,
-       FontInfo const & font, ColorCode back, ColorCode frame)
+       FontInfo const & font, Color back, Color frame)
 {
        int width;
        int ascent;
@@ -517,17 +569,37 @@ int GuiPainter::preeditText(int x, int y, char_type c,
 }
 
 
-void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
+void GuiPainter::underline(FontInfo const & f, int x, int y, int width,
+                           line_style ls)
 {
        FontMetrics const & fm = theFontMetrics(f);
+       int const pos = fm.underlinePos();
 
-       int const below = max(fm.maxDescent() / 2, 2);
-       int const height = max((fm.maxDescent() / 4) - 1, 1);
+       line(x, y + pos, x + width, y + pos,
+            f.realColor(), ls, fm.lineWidth());
+}
 
-       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 pos = fm.strikeoutPos();
+
+       line(x, y - pos, x + width, y - pos,
+            f.realColor(), line_solid, fm.lineWidth());
+}
+
+
+void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
+{
+       FontMetrics const & fm = theFontMetrics(f);
+       int const pos1 = fm.underlinePos() + fm.lineWidth();
+       int const pos2 = fm.underlinePos() - fm.lineWidth() + 1;
+
+       line(x, y + pos1, x + width, y + pos1,
+                f.realColor(), line_solid, fm.lineWidth());
+       line(x, y + pos2, x + width, y + pos2,
+                f.realColor(), line_solid, fm.lineWidth());
 }
 
 
@@ -545,5 +617,24 @@ void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
                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 = 2;
+       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 + step/2, y + height);
+               x += step/2;
+       }
+       //setRenderHint(Antialiasing, false);
+}
+
 } // namespace frontend
 } // namespace lyx