]> git.lyx.org Git - lyx.git/blobdiff - src/frontends/qt4/GuiPainter.cpp
More GuiInfo usability work
[lyx.git] / src / frontends / qt4 / GuiPainter.cpp
index 5329624a6a1b6f2605f0713fa1c6bbcf10e6cb1e..2c6529a9381046fde8a9dfb9ea81cd7083f1c0ce 100644 (file)
 #include "qt_helpers.h"
 
 #include "Font.h"
-#include "Language.h"
 #include "LyXRC.h"
 
-#include "insets/Inset.h"
-
-#include "support/lassert.h"
 #include "support/debug.h"
+#include "support/lassert.h"
+#include "support/lyxlib.h"
+
+#include <algorithm>
 
 #include <QPixmapCache>
 #include <QTextLayout>
@@ -52,10 +52,10 @@ 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_ = thin_line;
+       // set cache correctly
+       current_color_ = pen().color();
+       current_ls_ = pen().style() == Qt::DotLine ? line_onoffdash : line_solid;
+       current_lw_ = pen().width();
 }
 
 
@@ -80,8 +80,11 @@ void GuiPainter::setQPainterPen(QColor const & col,
        pen.setColor(col);
 
        switch (ls) {
-               case line_solid: pen.setStyle(Qt::SolidLine); break;
-               case line_onoffdash: pen.setStyle(Qt::DotLine); break;
+       case line_solid:
+       case line_solid_aliased:
+               pen.setStyle(Qt::SolidLine); break;
+       case line_onoffdash:
+               pen.setStyle(Qt::DotLine); break;
        }
 
        pen.setWidth(lw);
@@ -131,15 +134,15 @@ QColor GuiPainter::filterColor(QColor const & 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,
@@ -168,9 +171,6 @@ void GuiPainter::leaveMonochromeMode()
 
 void GuiPainter::point(int x, int y, Color col)
 {
-       if (!isDrawingEnabled())
-               return;
-
        setQPainterPen(computeColor(col));
        drawPoint(x, y);
 }
@@ -181,12 +181,9 @@ void GuiPainter::line(int x1, int y1, int x2, int y2,
        line_style ls,
        int lw)
 {
-       if (!isDrawingEnabled())
-               return;
-
        setQPainterPen(computeColor(col), ls, lw);
        bool const do_antialiasing = renderHints() & TextAntialiasing
-               && x1 != x2 && y1 != y2;
+               && x1 != x2 && y1 != y2 && ls != line_solid_aliased;
        setRenderHint(Antialiasing, do_antialiasing);
        drawLine(x1, y1, x2, y2);
        setRenderHint(Antialiasing, false);
@@ -199,15 +196,14 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
        line_style ls,
        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);
 
+       // Note: the proper way to not get blurry vertical and horizontal lines is
+       // to add 0.5 to all coordinates.
        bool antialias = false;
        for (int i = 0; i < np; ++i) {
                points[i].setX(xp[i]);
@@ -218,7 +214,8 @@ void GuiPainter::lines(int const * xp, int const * yp, int np,
        QColor const color = computeColor(col);
        setQPainterPen(color, ls, lw);
        bool const text_is_antialiased = renderHints() & TextAntialiasing;
-       setRenderHint(Antialiasing, antialias && text_is_antialiased);
+       setRenderHint(Antialiasing,
+                     antialias && text_is_antialiased && ls != line_solid_aliased);
        if (fs == fill_none) {
                drawPolyline(points.data(), np);
        } else {
@@ -241,9 +238,6 @@ void GuiPainter::path(int const * xp, int const * yp,
        line_style ls,
        int lw)
 {
-       if (!isDrawingEnabled())
-               return;
-
        QPainterPath bpath;
        // This is the starting point, so its control points are meaningless
        bpath.moveTo(xp[0], yp[0]);
@@ -259,7 +253,7 @@ void GuiPainter::path(int const * xp, int const * yp,
        QColor const color = computeColor(col);
        setQPainterPen(color, ls, lw);
        bool const text_is_antialiased = renderHints() & TextAntialiasing;
-       setRenderHint(Antialiasing, text_is_antialiased);
+       setRenderHint(Antialiasing, text_is_antialiased && ls != line_solid_aliased);
        drawPath(bpath);
        if (fs != fill_none)
                fillPath(bpath, QBrush(color));
@@ -272,9 +266,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
        line_style ls,
        int lw)
 {
-       if (!isDrawingEnabled())
-               return;
-
        setQPainterPen(computeColor(col), ls, lw);
        drawRect(x, y, w, h);
 }
@@ -282,9 +273,6 @@ void GuiPainter::rectangle(int x, int y, int w, int h,
 
 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));
 }
 
@@ -292,9 +280,6 @@ void GuiPainter::fillRectangle(int x, int y, int w, int h, 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(computeColor(col));
        bool const do_antialiasing = renderHints() & TextAntialiasing;
@@ -311,9 +296,6 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
 
        fillRectangle(x, y, w, h, Color_graphicsbg);
 
-       if (!isDrawingEnabled())
-               return;
-
        QImage const image = qlimage.image();
        QRectF const drect = QRectF(x, y, w, h);
        QRectF const srect = QRectF(0, 0, image.width(), image.height());
@@ -327,13 +309,21 @@ void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
 }
 
 
-int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
+void GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
 {
-       return text(x, y, docstring(1, c), f);
+       text(x, y, docstring(1, c), f);
 }
 
 
-void GuiPainter::do_drawText(int x, int y, QString str, bool rtl, FontInfo const & f, QFont ff)
+void GuiPainter::text(int x, int y, docstring const & s, FontInfo const & f)
+{
+       text(x, y, s, f, Auto, 0.0, 0.0);
+}
+
+
+void GuiPainter::do_drawText(int x, int y, QString str,
+                             GuiPainter::Direction const dir,
+                             FontInfo const & f, QFont ff)
 {
        setQPainterPen(computeColor(f.realColor()));
        if (font() != ff)
@@ -346,10 +336,10 @@ void GuiPainter::do_drawText(int x, int y, QString str, bool rtl, FontInfo const
        /* Use unicode override characters to enforce drawing direction
         * Source: http://www.iamcal.com/understanding-bidirectional-text/
         */
-       if (rtl)
+       if (dir == RtL)
                // Right-to-left override: forces to draw text right-to-left
                str = QChar(0x202E) + str;
-       else
+       else if (dir == LtR)
                // Left-to-right override: forces to draw text left-to-right
                str =  QChar(0x202D) + str;
        drawText(x, y, str);
@@ -360,21 +350,25 @@ void GuiPainter::do_drawText(int x, int y, QString str, bool rtl, FontInfo const
         * 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,
+       int flag = 0;
+       if (dir == RtL)
+               flag = Qt::TextForceRightToLeft;
+       else if (dir == LtR)
+               flag = Qt::TextForceLeftToRight;
+       drawText(x + ((dir == RtL) ? textwidth : 0), y - fm.maxAscent(), 0, 0,
                 flag | Qt::TextDontClip,
                 str);
 #endif
 }
 
 
-int GuiPainter::text(int x, int y, docstring const & s,
-                     FontInfo const & f, bool const rtl,
-                     double const wordspacing)
+void GuiPainter::text(int x, int y, docstring const & s,
+                      FontInfo const & f, Direction const dir,
+                      double const wordspacing, double const tw)
 {
        //LYXERR0("text: x=" << x << ", s=" << s);
        if (s.empty())
-               return 0;
+               return;
 
        /* Caution: The following ucs4 to QString conversions work for symbol fonts
        only because they are no real conversions but simple casts in reality.
@@ -382,8 +376,8 @@ int GuiPainter::text(int x, int y, docstring const & s,
        of the symbol in the font (as given in lib/symbols) as a char_type to the
        frontend. This is just wrong, because the symbol is no UCS4 character at
        all. You can think of this number as the code point of the symbol in a
-       custom symbol encoding. It works because this char_type is lateron again
-       interpreted as a position in the font again.
+       custom symbol encoding. It works because this char_type is later on again
+       interpreted as a position in the font.
        The correct solution would be to have extra functions for symbols, but that
        would require to duplicate a lot of frontend and mathed support code.
        */
@@ -400,14 +394,13 @@ int GuiPainter::text(int x, int y, docstring const & s,
        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
-       // Note that we have to take in account space stretching (word spacing)
-       int const textwidth = fm.width(s) + count(s.begin(), s.end(), ' ') * wordspacing;
-
-       if (!isDrawingEnabled())
-               return textwidth;
+       int textwidth = 0;
+       if (tw == 0.0)
+               // Note that we have to take in account space stretching (word spacing)
+               textwidth = fm.width(s) +
+                       static_cast<int>(fm.countExpanders(s) * wordspacing);
+       else
+               textwidth = static_cast<int>(tw);
 
        textDecoration(f, x, y, textwidth);
 
@@ -428,7 +421,7 @@ int GuiPainter::text(int x, int y, docstring const & s,
                if (QPixmapCache::find(key, pm)) {
                        // Draw the cached pixmap.
                        drawPixmap(x + lb, y - mA, pm);
-                       return textwidth;
+                       return;
                }
 
                // Only the right bearing of the last character is
@@ -446,7 +439,7 @@ int GuiPainter::text(int x, int y, docstring const & s,
 #endif
                        pm.fill(Qt::transparent);
                        GuiPainter p(&pm, pixelRatio());
-                       p.do_drawText(-lb, mA, str, rtl, f, ff);
+                       p.do_drawText(-lb, mA, str, dir, f, ff);
                        QPixmapCache::insert(key, pm);
                        //LYXERR(Debug::PAINTING, "h=" << h << "  mA=" << mA << "  mD=" << mD
                        //      << "  w=" << w << "  lb=" << lb << "  tw=" << textwidth
@@ -456,37 +449,47 @@ int GuiPainter::text(int x, int y, docstring const & s,
                        drawPixmap(x + lb, y - mA, pm);
                        //rectangle(x-lb, y-mA, w, h, Color_green);
                }
-               return textwidth;
+               return;
        }
 
-       // don't use the pixmap cache,
-       do_drawText(x, y, str, rtl, f, ff);
+       // don't use the pixmap cache
+       setQPainterPen(computeColor(f.realColor()));
+       if (dir != Auto) {
+               shared_ptr<QTextLayout const> ptl =
+                       fm.getTextLayout(s, dir == RtL, wordspacing);
+               ptl->draw(this, QPointF(x, y - fm.maxAscent()));
+       }
+       else {
+               if (font() != ff)
+                       setFont(ff);
+               drawText(x, y, str);
+       }
        //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)
+void GuiPainter::text(int x, int y, docstring const & str, Font const & f,
+                      double const wordspacing, double const tw)
 {
-       return text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft(), wordspacing);
+       text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft() ? RtL : LtR,
+            wordspacing, tw);
 }
 
 
-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)
+void 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, double const tw)
 {
        GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
        FontInfo fi = f.fontInfo();
-       bool const rtl = f.isVisibleRightToLeft();
+       Direction const dir = f.isVisibleRightToLeft() ? RtL : LtR;
 
        // 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);
+       int xmin = fm.pos2x(str, from, dir == RtL, wordspacing);
+       int xmax = fm.pos2x(str, to, dir == RtL, wordspacing);
        if (xmin > xmax)
                swap(xmin, xmax);
 
@@ -495,7 +498,7 @@ int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
        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);
+       text(x, y, str, fi, dir, wordspacing, tw);
 
        // Then the part in normal color
        // Note that in Qt5, it is not possible to use Qt::UniteClip,
@@ -503,10 +506,8 @@ int GuiPainter::text(int x, int y, docstring const & str, Font const & f,
        fi.setPaintColor(orig);
        QRegion region(viewport());
        setClipRegion(region - clip);
-       text(x, y, str, fi, rtl, wordspacing);
+       text(x, y, str, fi, dir, wordspacing, tw);
        setClipping(false);
-
-       return textwidth;
 }
 
 
@@ -516,6 +517,8 @@ void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
                underline(f, x, y, width);
        if (f.strikeout() == FONT_ON)
                strikeoutLine(f, x, y, width);
+       if (f.xout() == FONT_ON)
+               crossoutLines(f, x, y, width);
        if (f.uuline() == FONT_ON)
                doubleUnderline(f, x, y, width);
        if (f.uwave() == FONT_ON)
@@ -527,31 +530,10 @@ void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
 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;
+       int width, ascent, descent;
 
        FontMetrics const & fm = theFontMetrics(font);
        fm.rectText(str, width, ascent, descent);
@@ -563,24 +545,25 @@ void GuiPainter::rectText(int x, int y, docstring const & str,
        if (frame != Color_none)
                rectangle(x, y - ascent, width, ascent + descent, frame);
 
+       // FIXME: let offset depend on font
        text(x + 3, y, str, font);
 }
 
 
-void GuiPainter::buttonText(int x, int y, docstring const & str,
-       FontInfo const & font, bool mouseHover)
+void GuiPainter::buttonText(int x, int baseline, docstring const & s,
+       FontInfo const & font, Color back, Color frame, int offset)
 {
-       int width;
-       int ascent;
-       int descent;
+       int width, ascent, descent;
 
        FontMetrics const & fm = theFontMetrics(font);
-       fm.buttonText(str, width, ascent, descent);
+       fm.buttonText(s, offset, width, ascent, descent);
 
-       static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
+       static int const d = offset / 2;
 
-       button(x + d, y - ascent, width - Inset::TEXT_TO_INSET_OFFSET, descent + ascent, mouseHover);
-       text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
+       fillRectangle(x + d + 1, baseline - ascent + 1, width - offset - 1,
+                             ascent + descent - 1, back);
+       rectangle(x + d, baseline - ascent, width - offset, ascent + descent, frame);
+       text(x + offset, baseline, s, font);
 }
 
 
@@ -638,6 +621,20 @@ void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
 }
 
 
+void GuiPainter::crossoutLines(FontInfo const & f, int x, int y, int width)
+{
+       FontInfo tmpf = f;
+       tmpf.setXout(FONT_OFF);
+
+       // the definition of \xout in ulem.sty is
+    //  \def\xout{\bgroup \markoverwith{\hbox to.35em{\hss/\hss}}\ULon}
+       // Let's mimick it somewhat.
+       double offset = max(0.35 * theFontMetrics(tmpf).em(), 1);
+       for (int i = 0 ; i < iround(width / offset) ; ++i)
+               text(x + iround(i * offset), y, '/', tmpf);
+}
+
+
 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
 {
        FontMetrics const & fm = theFontMetrics(f);