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 // This is needed for some math glyphs.
356 // FIXME In texted, this behaves differently depending
357 // on lyxrc.force_paint_single_char status.
358 // Should the soft hyphen char be displayed at all?
359 // I don't think so (i.e., Qt is correct as far as
360 // texted is concerned). /spitz
361 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
362 setQPainterPen(computeColor(f.realColor()));
363 QTextLayout adsymbol(str);
364 adsymbol.setFont(ff);
365 adsymbol.beginLayout();
366 QTextLine line = adsymbol.createLine();
367 line.setNumColumns(1);
368 line.setPosition(QPointF(0, -line.ascent()));
369 adsymbol.endLayout();
370 line.draw(this, QPointF(x, y));
374 if (use_pixmap_cache_) {
376 QString key = generateStringSignature(str, f);
378 // Warning: Left bearing is in general negative! Only the case
379 // where left bearing is negative is of interest WRT the
380 // pixmap width and the text x-position.
381 // Only the left bearing of the first character is important
382 // as we always write from left to right, even for
383 // right-to-left languages.
384 int const lb = min(fm.lbearing(s[0]), 0);
385 int const mA = fm.maxAscent();
386 if (QPixmapCache::find(key, pm)) {
387 // Draw the cached pixmap.
388 drawPixmap(x + lb, y - mA, pm);
392 // Only the right bearing of the last character is
393 // important as we always write from left to right,
394 // even for right-to-left languages.
395 int const rb = fm.rbearing(s[s.size()-1]);
396 int const w = textwidth + rb - lb;
397 int const mD = fm.maxDescent();
398 int const h = mA + mD;
399 if (w > 0 && h > 0) {
401 pm.fill(Qt::transparent);
403 p.setQPainterPen(computeColor(f.realColor()));
406 // We need to draw the text as LTR as we use our own bidi code.
407 p.setLayoutDirection(Qt::LeftToRight);
408 p.drawText(-lb, mA, str);
409 QPixmapCache::insert(key, pm);
410 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
411 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
414 // Draw the new cached pixmap.
415 drawPixmap(x + lb, y - mA, pm);
421 // don't use the pixmap cache,
422 // draw directly onto the painting device
423 setQPainterPen(computeColor(f.realColor()));
426 // We need to draw the text as LTR as we use our own bidi code.
427 QPainter::setLayoutDirection(Qt::LeftToRight);
429 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
430 // << " at " << x << "," << y);
435 void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
437 if (f.underbar() == FONT_ON)
438 underline(f, x, y, width);
439 if (f.strikeout() == FONT_ON)
440 strikeoutLine(f, x, y, width);
441 if (f.uuline() == FONT_ON)
442 doubleUnderline(f, x, y, width);
443 if (f.uwave() == FONT_ON)
444 // f.color() doesn't work on some circumstances
445 wavyHorizontalLine(x, y, width, f.realColor().baseColor);
449 static int max(int a, int b) { return a > b ? a : b; }
452 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
455 fillRectangle(x, y, w, h, Color_buttonhoverbg);
457 fillRectangle(x, y, w, h, Color_buttonbg);
458 buttonFrame(x, y, w, h);
462 void GuiPainter::buttonFrame(int x, int y, int w, int h)
464 line(x, y, x, y + h - 1, Color_buttonframe);
465 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
466 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
467 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
471 void GuiPainter::rectText(int x, int y, docstring const & str,
472 FontInfo const & font, Color back, Color frame)
478 FontMetrics const & fm = theFontMetrics(font);
479 fm.rectText(str, width, ascent, descent);
481 if (back != Color_none)
482 fillRectangle(x + 1, y - ascent + 1, width - 1,
483 ascent + descent - 1, back);
485 if (frame != Color_none)
486 rectangle(x, y - ascent, width, ascent + descent, frame);
488 text(x + 3, y, str, font);
492 void GuiPainter::buttonText(int x, int y, docstring const & str,
493 FontInfo const & font, bool mouseHover)
499 FontMetrics const & fm = theFontMetrics(font);
500 fm.buttonText(str, width, ascent, descent);
502 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
504 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
505 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
509 int GuiPainter::preeditText(int x, int y, char_type c,
510 FontInfo const & font, preedit_style style)
512 FontInfo temp_font = font;
513 FontMetrics const & fm = theFontMetrics(font);
514 int ascent = fm.maxAscent();
515 int descent = fm.maxDescent();
516 int height = ascent + descent;
517 int width = fm.width(c);
520 case preedit_default:
521 // default unselecting mode.
522 fillRectangle(x, y - height + 1, width, height, Color_background);
523 dashedUnderline(font, x, y - descent + 1, width);
525 case preedit_selecting:
526 // We are in selecting mode: white text on black background.
527 fillRectangle(x, y - height + 1, width, height, Color_black);
528 temp_font.setColor(Color_white);
531 // The character comes with a cursor.
532 fillRectangle(x, y - height + 1, width, height, Color_background);
533 underline(font, x, y - descent + 1, width);
536 text(x, y - descent + 1, c, temp_font);
542 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
544 FontMetrics const & fm = theFontMetrics(f);
546 int const below = max(fm.maxDescent() / 2, 2);
548 line(x, y + below, x + width, y + below, f.realColor());
549 line(x, y + below - 2, x + width, y + below - 2, f.realColor());
553 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
555 FontMetrics const & fm = theFontMetrics(f);
557 int const below = max(fm.maxDescent() / 2, 2);
558 int const height = max((fm.maxDescent() / 4) - 1, 1);
561 line(x, y + below, x + width, y + below, f.realColor());
563 fillRectangle(x, y + below, width, below + height, f.realColor());
567 void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
569 FontMetrics const & fm = theFontMetrics(f);
571 int const middle = max((fm.maxHeight() / 4), 1);
572 int const height = middle/3;
575 line(x, y - middle, x + width, y - middle, f.realColor());
577 fillRectangle(x, y - middle, width, height, f.realColor());
581 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
583 FontMetrics const & fm = theFontMetrics(f);
585 int const below = max(fm.maxDescent() / 2, 2);
586 int height = max((fm.maxDescent() / 4) - 1, 1);
591 for (int n = 0; n != height; ++n)
592 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
596 void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
598 setQPainterPen(computeColor(col));
600 int const xend = x + width;
602 //FIXME: I am not sure if Antialiasing gives the best effect.
603 //setRenderHint(Antialiasing, true);
606 drawLine(x, y - height, x + step, y + height);
608 drawLine(x, y + height, x + step/2, y + height);
611 //setRenderHint(Antialiasing, false);
614 } // namespace frontend