3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
7 * \author Abdelrazak Younes
9 * Full author contact details are available in file CREDITS.
14 #include "GuiPainter.h"
16 #include "ColorCache.h"
17 #include "GuiApplication.h"
18 #include "GuiFontLoader.h"
19 #include "GuiFontMetrics.h"
21 #include "qt_helpers.h"
27 #include "insets/Inset.h"
29 #include "support/lassert.h"
30 #include "support/debug.h"
32 #include <QPixmapCache>
33 #include <QTextLayout>
35 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
36 // drawing text. This is especially useful for older PPC/Mac systems.
38 #define USE_PIXMAP_CACHE 0
40 #define USE_PIXMAP_CACHE 1
48 const float Painter::thin_line = 0.0;
50 GuiPainter::GuiPainter(QPaintDevice * device)
51 : QPainter(device), Painter(),
52 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
54 // new QPainter has default QPen:
55 current_color_ = guiApp->colorCache().get(Color_black);
56 current_ls_ = line_solid;
57 current_lw_ = thin_line;
61 GuiPainter::~GuiPainter()
64 //lyxerr << "GuiPainter::end()" << endl;
68 void GuiPainter::setQPainterPen(QColor const & col,
69 Painter::line_style ls, float lw)
71 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
78 QPen pen = QPainter::pen();
82 case line_solid: pen.setStyle(Qt::SolidLine); break;
83 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
92 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
95 sig.append(QChar(static_cast<short>(f.family())));
96 sig.append(QChar(static_cast<short>(f.series())));
97 sig.append(QChar(static_cast<short>(f.realShape())));
98 sig.append(QChar(static_cast<short>(f.size())));
99 Color const & color = f.realColor();
100 sig.append(QChar(static_cast<short>(color.baseColor)));
101 sig.append(QChar(static_cast<short>(color.mergeColor)));
102 if (!monochrome_min_.empty()) {
103 QColor const & min = monochrome_min_.top();
104 QColor const & max = monochrome_max_.top();
105 sig.append(QChar(static_cast<short>(min.red())));
106 sig.append(QChar(static_cast<short>(min.green())));
107 sig.append(QChar(static_cast<short>(min.blue())));
108 sig.append(QChar(static_cast<short>(max.red())));
109 sig.append(QChar(static_cast<short>(max.green())));
110 sig.append(QChar(static_cast<short>(max.blue())));
116 QColor GuiPainter::computeColor(Color col)
118 return filterColor(guiApp->colorCache().get(col));
122 QColor GuiPainter::filterColor(QColor const & col)
124 if (monochrome_min_.empty())
127 // map into [min,max] interval
128 QColor const & min = monochrome_min_.top();
129 QColor const & max = monochrome_max_.top();
131 qreal v = col.valueF();
132 v *= v; // make it a bit steeper (i.e. darker)
134 qreal minr, ming, minb;
135 qreal maxr, maxg, maxb;
136 min.getRgbF(&minr, &ming, &minb);
137 max.getRgbF(&maxr, &maxg, &maxb);
141 v * (minr - maxr) + maxr,
142 v * (ming - maxg) + maxg,
143 v * (minb - maxb) + maxb);
148 void GuiPainter::enterMonochromeMode(Color const & min, Color const & max)
150 QColor qmin = filterColor(guiApp->colorCache().get(min));
151 QColor qmax = filterColor(guiApp->colorCache().get(max));
152 monochrome_min_.push(qmin);
153 monochrome_max_.push(qmax);
157 void GuiPainter::leaveMonochromeMode()
159 LASSERT(!monochrome_min_.empty(), return);
160 monochrome_min_.pop();
161 monochrome_max_.pop();
165 void GuiPainter::point(int x, int y, Color col)
167 if (!isDrawingEnabled())
170 setQPainterPen(computeColor(col));
175 void GuiPainter::line(int x1, int y1, int x2, int y2,
180 if (!isDrawingEnabled())
183 setQPainterPen(computeColor(col), ls, lw);
184 bool const do_antialiasing = renderHints() & TextAntialiasing
185 && x1 != x2 && y1 != y2;
186 setRenderHint(Antialiasing, do_antialiasing);
187 drawLine(x1, y1, x2, y2);
188 setRenderHint(Antialiasing, false);
192 void GuiPainter::lines(int const * xp, int const * yp, int np,
197 if (!isDrawingEnabled())
200 // double the size if needed
201 static QVector<QPoint> points(32);
202 if (np > points.size())
203 points.resize(2 * np);
205 bool antialias = false;
206 for (int i = 0; i < np; ++i) {
207 points[i].setX(xp[i]);
208 points[i].setY(yp[i]);
210 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
212 setQPainterPen(computeColor(col), ls, lw);
213 bool const text_is_antialiased = renderHints() & TextAntialiasing;
214 setRenderHint(Antialiasing, antialias && text_is_antialiased);
215 drawPolyline(points.data(), np);
216 setRenderHint(Antialiasing, false);
220 void GuiPainter::rectangle(int x, int y, int w, int h,
225 if (!isDrawingEnabled())
228 setQPainterPen(computeColor(col), ls, lw);
229 drawRect(x, y, w, h);
233 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
235 if (!isDrawingEnabled())
238 fillRect(x, y, w, h, guiApp->colorCache().get(col));
242 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
243 int a1, int a2, Color col)
245 if (!isDrawingEnabled())
248 // LyX usings 1/64ths degree, Qt usings 1/16th
249 setQPainterPen(computeColor(col));
250 bool const do_antialiasing = renderHints() & TextAntialiasing;
251 setRenderHint(Antialiasing, do_antialiasing);
252 drawArc(x, y, w, h, a1 / 4, a2 / 4);
253 setRenderHint(Antialiasing, false);
257 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
259 graphics::GuiImage const & qlimage =
260 static_cast<graphics::GuiImage const &>(i);
262 fillRectangle(x, y, w, h, Color_graphicsbg);
264 if (!isDrawingEnabled())
267 drawImage(x, y, qlimage.image(), 0, 0, w, h);
271 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
274 return text(x, y, s, f);
278 int GuiPainter::smallCapsText(int x, int y,
279 QString const & s, FontInfo const & f)
281 FontInfo smallfont(f);
282 smallfont.decSize().decSize().setShape(UP_SHAPE);
284 QFont const & qfont = getFont(f);
285 QFont const & qsmallfont = getFont(smallfont);
287 setQPainterPen(computeColor(f.realColor()));
289 size_t const ls = s.length();
290 for (unsigned int i = 0; i < ls; ++i) {
291 QChar const c = s[i].toUpper();
297 if (isDrawingEnabled())
298 drawText(x + textwidth, y, c);
299 textwidth += fontMetrics().width(c);
305 int GuiPainter::text(int x, int y, docstring const & s,
311 /* Caution: The following ucs4 to QString conversions work for symbol fonts
312 only because they are no real conversions but simple casts in reality.
313 When we want to draw a symbol or calculate the metrics we pass the position
314 of the symbol in the font (as given in lib/symbols) as a char_type to the
315 frontend. This is just wrong, because the symbol is no UCS4 character at
316 all. You can think of this number as the code point of the symbol in a
317 custom symbol encoding. It works because this char_type is lateron again
318 interpreted as a position in the font again.
319 The correct solution would be to have extra functions for symbols, but that
320 would require to duplicate a lot of frontend and mathed support code.
322 QString str = toqstr(s);
325 // HACK: QT3 refuses to show single compose characters
326 // Still needed with Qt4?
327 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
331 QFont const & ff = getFont(f);
332 GuiFontMetrics const & fm = getFontMetrics(f);
336 if (f.realShape() == SMALLCAPS_SHAPE) {
337 textwidth = smallCapsText(x, y, str, f);
338 textDecoration(f, x, y, textwidth);
342 // Here we use the font width cache instead of
343 // textwidth = fontMetrics().width(str);
344 // because the above is awfully expensive on MacOSX
345 textwidth = fm.width(s);
346 textDecoration(f, x, y, textwidth);
348 if (!isDrawingEnabled())
351 // Qt4 does not display a glyph whose codepoint is the
352 // same as that of a soft-hyphen (0x00ad), unless it
353 // occurs at a line-break. As a kludge, we force Qt to
354 // render this glyph using a one-column line.
355 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
356 setQPainterPen(computeColor(f.realColor()));
357 QTextLayout adsymbol(str);
358 adsymbol.setFont(ff);
359 adsymbol.beginLayout();
360 QTextLine line = adsymbol.createLine();
361 line.setNumColumns(1);
362 line.setPosition(QPointF(0, -line.ascent()));
363 adsymbol.endLayout();
364 line.draw(this, QPointF(x, y));
368 if (use_pixmap_cache_) {
370 QString key = generateStringSignature(str, f);
372 // Warning: Left bearing is in general negative! Only the case
373 // where left bearing is negative is of interest WRT the
374 // pixmap width and the text x-position.
375 // Only the left bearing of the first character is important
376 // as we always write from left to right, even for
377 // right-to-left languages.
378 int const lb = min(fm.lbearing(s[0]), 0);
379 int const mA = fm.maxAscent();
380 if (QPixmapCache::find(key, pm)) {
381 // Draw the cached pixmap.
382 drawPixmap(x + lb, y - mA, pm);
386 // Only the right bearing of the last character is
387 // important as we always write from left to right,
388 // even for right-to-left languages.
389 int const rb = fm.rbearing(s[s.size()-1]);
390 int const w = textwidth + rb - lb;
391 int const mD = fm.maxDescent();
392 int const h = mA + mD;
393 if (w > 0 && h > 0) {
395 pm.fill(Qt::transparent);
397 p.setQPainterPen(computeColor(f.realColor()));
400 // We need to draw the text as LTR as we use our own bidi code.
401 p.setLayoutDirection(Qt::LeftToRight);
402 p.drawText(-lb, mA, str);
403 QPixmapCache::insert(key, pm);
404 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
405 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
408 // Draw the new cached pixmap.
409 drawPixmap(x + lb, y - mA, pm);
415 // don't use the pixmap cache,
416 // draw directly onto the painting device
417 setQPainterPen(computeColor(f.realColor()));
420 // We need to draw the text as LTR as we use our own bidi code.
421 QPainter::setLayoutDirection(Qt::LeftToRight);
423 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
424 // << " at " << x << "," << y);
429 void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
431 if (f.underbar() == FONT_ON)
432 underline(f, x, y, width);
433 if (f.strikeout() == FONT_ON)
434 strikeoutLine(f, x, y, width);
435 if (f.uuline() == FONT_ON)
436 doubleUnderline(f, x, y, width);
437 if (f.uwave() == FONT_ON)
438 // f.color() doesn't work on some circumstances
439 wavyHorizontalLine(x, y, width, f.realColor().baseColor);
443 static int max(int a, int b) { return a > b ? a : b; }
446 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
449 fillRectangle(x, y, w, h, Color_buttonhoverbg);
451 fillRectangle(x, y, w, h, Color_buttonbg);
452 buttonFrame(x, y, w, h);
456 void GuiPainter::buttonFrame(int x, int y, int w, int h)
458 line(x, y, x, y + h - 1, Color_buttonframe);
459 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
460 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
461 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
465 void GuiPainter::rectText(int x, int y, docstring const & str,
466 FontInfo const & font, Color back, Color frame)
472 FontMetrics const & fm = theFontMetrics(font);
473 fm.rectText(str, width, ascent, descent);
475 if (back != Color_none)
476 fillRectangle(x + 1, y - ascent + 1, width - 1,
477 ascent + descent - 1, back);
479 if (frame != Color_none)
480 rectangle(x, y - ascent, width, ascent + descent, frame);
482 text(x + 3, y, str, font);
486 void GuiPainter::buttonText(int x, int y, docstring const & str,
487 FontInfo const & font, bool mouseHover)
493 FontMetrics const & fm = theFontMetrics(font);
494 fm.buttonText(str, width, ascent, descent);
496 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
498 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
499 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
503 int GuiPainter::preeditText(int x, int y, char_type c,
504 FontInfo const & font, preedit_style style)
506 FontInfo temp_font = font;
507 FontMetrics const & fm = theFontMetrics(font);
508 int ascent = fm.maxAscent();
509 int descent = fm.maxDescent();
510 int height = ascent + descent;
511 int width = fm.width(c);
514 case preedit_default:
515 // default unselecting mode.
516 fillRectangle(x, y - height + 1, width, height, Color_background);
517 dashedUnderline(font, x, y - descent + 1, width);
519 case preedit_selecting:
520 // We are in selecting mode: white text on black background.
521 fillRectangle(x, y - height + 1, width, height, Color_black);
522 temp_font.setColor(Color_white);
525 // The character comes with a cursor.
526 fillRectangle(x, y - height + 1, width, height, Color_background);
527 underline(font, x, y - descent + 1, width);
530 text(x, y - descent + 1, c, temp_font);
536 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
538 FontMetrics const & fm = theFontMetrics(f);
540 int const below = max(fm.maxDescent() / 2, 2);
542 line(x, y + below, x + width, y + below, f.realColor());
543 line(x, y + below - 2, x + width, y + below - 2, f.realColor());
547 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
549 FontMetrics const & fm = theFontMetrics(f);
551 int const below = max(fm.maxDescent() / 2, 2);
552 int const height = max((fm.maxDescent() / 4) - 1, 1);
555 line(x, y + below, x + width, y + below, f.realColor());
557 fillRectangle(x, y + below, width, below + height, f.realColor());
561 void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
563 FontMetrics const & fm = theFontMetrics(f);
565 int const middle = max((fm.maxHeight() / 4), 1);
566 int const height = middle/3;
569 line(x, y - middle, x + width, y - middle, f.realColor());
571 fillRectangle(x, y - middle, width, height, f.realColor());
575 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
577 FontMetrics const & fm = theFontMetrics(f);
579 int const below = max(fm.maxDescent() / 2, 2);
580 int height = max((fm.maxDescent() / 4) - 1, 1);
585 for (int n = 0; n != height; ++n)
586 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
590 void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
592 setQPainterPen(computeColor(col));
594 int const xend = x + width;
596 //FIXME: I am not sure if Antialiasing gives the best effect.
597 //setRenderHint(Antialiasing, true);
600 drawLine(x, y - height, x + step, y + height);
602 drawLine(x, y + height, x + step/2, y + height);
605 //setRenderHint(Antialiasing, false);
608 } // namespace frontend