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
202 static QVector<QPoint> points(32);
203 if (np > points.size())
204 points.resize(2 * np);
206 bool antialias = false;
207 for (int i = 0; i < np; ++i) {
208 points[i].setX(xp[i]);
209 points[i].setY(yp[i]);
211 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
213 setQPainterPen(computeColor(col), ls, lw);
214 bool const text_is_antialiased = renderHints() & TextAntialiasing;
215 setRenderHint(Antialiasing, antialias && text_is_antialiased);
216 drawPolyline(points.data(), np);
217 setRenderHint(Antialiasing, false);
221 void GuiPainter::rectangle(int x, int y, int w, int h,
226 if (!isDrawingEnabled())
229 setQPainterPen(computeColor(col), ls, lw);
230 drawRect(x, y, w, h);
234 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
236 if (!isDrawingEnabled())
239 fillRect(x, y, w, h, guiApp->colorCache().get(col));
243 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
244 int a1, int a2, Color col)
246 if (!isDrawingEnabled())
249 // LyX usings 1/64ths degree, Qt usings 1/16th
250 setQPainterPen(computeColor(col));
251 bool const do_antialiasing = renderHints() & TextAntialiasing;
252 setRenderHint(Antialiasing, do_antialiasing);
253 drawArc(x, y, w, h, a1 / 4, a2 / 4);
254 setRenderHint(Antialiasing, false);
258 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
260 graphics::GuiImage const & qlimage =
261 static_cast<graphics::GuiImage const &>(i);
263 fillRectangle(x, y, w, h, Color_graphicsbg);
265 if (!isDrawingEnabled())
268 drawImage(x, y, qlimage.image(), 0, 0, w, h);
272 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
275 return text(x, y, s, f);
279 int GuiPainter::smallCapsText(int x, int y,
280 QString const & s, FontInfo const & f)
282 FontInfo smallfont(f);
283 smallfont.decSize().decSize().setShape(UP_SHAPE);
285 QFont const & qfont = getFont(f);
286 QFont const & qsmallfont = getFont(smallfont);
288 setQPainterPen(computeColor(f.realColor()));
290 size_t const ls = s.length();
291 for (unsigned int i = 0; i < ls; ++i) {
292 QChar const c = s[i].toUpper();
298 if (isDrawingEnabled())
299 drawText(x + textwidth, y, c);
300 textwidth += fontMetrics().width(c);
306 int GuiPainter::text(int x, int y, docstring const & s,
312 /* Caution: The following ucs4 to QString conversions work for symbol fonts
313 only because they are no real conversions but simple casts in reality.
314 When we want to draw a symbol or calculate the metrics we pass the position
315 of the symbol in the font (as given in lib/symbols) as a char_type to the
316 frontend. This is just wrong, because the symbol is no UCS4 character at
317 all. You can think of this number as the code point of the symbol in a
318 custom symbol encoding. It works because this char_type is lateron again
319 interpreted as a position in the font again.
320 The correct solution would be to have extra functions for symbols, but that
321 would require to duplicate a lot of frontend and mathed support code.
323 QString str = toqstr(s);
326 // HACK: QT3 refuses to show single compose characters
327 // Still needed with Qt4?
328 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
332 QFont const & ff = getFont(f);
333 GuiFontMetrics const & fm = getFontMetrics(f);
337 if (f.realShape() == SMALLCAPS_SHAPE) {
338 textwidth = smallCapsText(x, y, str, f);
339 textDecoration(f, x, y, textwidth);
343 // Here we use the font width cache instead of
344 // textwidth = fontMetrics().width(str);
345 // because the above is awfully expensive on MacOSX
346 textwidth = fm.width(s);
347 textDecoration(f, x, y, textwidth);
349 if (!isDrawingEnabled())
352 // Qt4 does not display a glyph whose codepoint is the
353 // same as that of a soft-hyphen (0x00ad), unless it
354 // occurs at a line-break. As a kludge, we force Qt to
355 // render this glyph using a one-column line.
356 // This is needed for some math glyphs.
357 // FIXME In texted, this behaves differently depending
358 // on lyxrc.force_paint_single_char status.
359 // Should the soft hyphen char be displayed at all?
360 // I don't think so (i.e., Qt is correct as far as
361 // texted is concerned). /spitz
362 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
363 setQPainterPen(computeColor(f.realColor()));
364 QTextLayout adsymbol(str);
365 adsymbol.setFont(ff);
366 adsymbol.beginLayout();
367 QTextLine line = adsymbol.createLine();
368 line.setNumColumns(1);
369 line.setPosition(QPointF(0, -line.ascent()));
370 adsymbol.endLayout();
371 line.draw(this, QPointF(x, y));
375 if (use_pixmap_cache_) {
377 QString key = generateStringSignature(str, f);
379 // Warning: Left bearing is in general negative! Only the case
380 // where left bearing is negative is of interest WRT the
381 // pixmap width and the text x-position.
382 // Only the left bearing of the first character is important
383 // as we always write from left to right, even for
384 // right-to-left languages.
385 int const lb = min(fm.lbearing(s[0]), 0);
386 int const mA = fm.maxAscent();
387 if (QPixmapCache::find(key, pm)) {
388 // Draw the cached pixmap.
389 drawPixmap(x + lb, y - mA, pm);
393 // Only the right bearing of the last character is
394 // important as we always write from left to right,
395 // even for right-to-left languages.
396 int const rb = fm.rbearing(s[s.size()-1]);
397 int const w = textwidth + rb - lb;
398 int const mD = fm.maxDescent();
399 int const h = mA + mD;
400 if (w > 0 && h > 0) {
402 pm.fill(Qt::transparent);
404 p.setQPainterPen(computeColor(f.realColor()));
407 // We need to draw the text as LTR as we use our own bidi code.
408 p.setLayoutDirection(Qt::LeftToRight);
409 p.drawText(-lb, mA, str);
410 QPixmapCache::insert(key, pm);
411 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
412 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
415 // Draw the new cached pixmap.
416 drawPixmap(x + lb, y - mA, pm);
422 // don't use the pixmap cache,
423 // draw directly onto the painting device
424 setQPainterPen(computeColor(f.realColor()));
427 // We need to draw the text as LTR as we use our own bidi code.
428 QPainter::setLayoutDirection(Qt::LeftToRight);
430 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
431 // << " at " << x << "," << y);
436 void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
438 if (f.underbar() == FONT_ON)
439 underline(f, x, y, width);
440 if (f.strikeout() == FONT_ON)
441 strikeoutLine(f, x, y, width);
442 if (f.uuline() == FONT_ON)
443 doubleUnderline(f, x, y, width);
444 if (f.uwave() == FONT_ON)
445 // f.color() doesn't work on some circumstances
446 wavyHorizontalLine(x, y, width, f.realColor().baseColor);
450 static int max(int a, int b) { return a > b ? a : b; }
453 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
456 fillRectangle(x, y, w, h, Color_buttonhoverbg);
458 fillRectangle(x, y, w, h, Color_buttonbg);
459 buttonFrame(x, y, w, h);
463 void GuiPainter::buttonFrame(int x, int y, int w, int h)
465 line(x, y, x, y + h - 1, Color_buttonframe);
466 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
467 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
468 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
472 void GuiPainter::rectText(int x, int y, docstring const & str,
473 FontInfo const & font, Color back, Color frame)
479 FontMetrics const & fm = theFontMetrics(font);
480 fm.rectText(str, width, ascent, descent);
482 if (back != Color_none)
483 fillRectangle(x + 1, y - ascent + 1, width - 1,
484 ascent + descent - 1, back);
486 if (frame != Color_none)
487 rectangle(x, y - ascent, width, ascent + descent, frame);
489 text(x + 3, y, str, font);
493 void GuiPainter::buttonText(int x, int y, docstring const & str,
494 FontInfo const & font, bool mouseHover)
500 FontMetrics const & fm = theFontMetrics(font);
501 fm.buttonText(str, width, ascent, descent);
503 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
505 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
506 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
510 int GuiPainter::preeditText(int x, int y, char_type c,
511 FontInfo const & font, preedit_style style)
513 FontInfo temp_font = font;
514 FontMetrics const & fm = theFontMetrics(font);
515 int ascent = fm.maxAscent();
516 int descent = fm.maxDescent();
517 int height = ascent + descent;
518 int width = fm.width(c);
521 case preedit_default:
522 // default unselecting mode.
523 fillRectangle(x, y - height + 1, width, height, Color_background);
524 dashedUnderline(font, x, y - descent + 1, width);
526 case preedit_selecting:
527 // We are in selecting mode: white text on black background.
528 fillRectangle(x, y - height + 1, width, height, Color_black);
529 temp_font.setColor(Color_white);
532 // The character comes with a cursor.
533 fillRectangle(x, y - height + 1, width, height, Color_background);
534 underline(font, x, y - descent + 1, width);
537 text(x, y - descent + 1, c, temp_font);
543 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
545 FontMetrics const & fm = theFontMetrics(f);
547 int const below = max(fm.maxDescent() / 2, 2);
549 line(x, y + below, x + width, y + below, f.realColor());
550 line(x, y + below - 2, x + width, y + below - 2, f.realColor());
554 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
556 FontMetrics const & fm = theFontMetrics(f);
558 int const below = max(fm.maxDescent() / 2, 2);
559 int const height = max((fm.maxDescent() / 4) - 1, 1);
562 line(x, y + below, x + width, y + below, f.realColor());
564 fillRectangle(x, y + below, width, below + height, f.realColor());
568 void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
570 FontMetrics const & fm = theFontMetrics(f);
572 int const middle = max((fm.maxHeight() / 4), 1);
573 int const height = middle/3;
576 line(x, y - middle, x + width, y - middle, f.realColor());
578 fillRectangle(x, y - middle, width, height, f.realColor());
582 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
584 FontMetrics const & fm = theFontMetrics(f);
586 int const below = max(fm.maxDescent() / 2, 2);
587 int height = max((fm.maxDescent() / 4) - 1, 1);
592 for (int n = 0; n != height; ++n)
593 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
597 void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
599 setQPainterPen(computeColor(col));
601 int const xend = x + width;
603 //FIXME: I am not sure if Antialiasing gives the best effect.
604 //setRenderHint(Antialiasing, true);
607 drawLine(x, y - height, x + step, y + height);
609 drawLine(x, y + height, x + step/2, y + height);
612 //setRenderHint(Antialiasing, false);
615 } // namespace frontend