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 "GuiApplication.h"
17 #include "GuiFontMetrics.h"
19 #include "qt_helpers.h"
24 #include "support/debug.h"
26 #include <QPixmapCache>
27 #include <QTextLayout>
29 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
30 // drawing text. This is especially useful for older PPC/Mac systems.
32 #define USE_PIXMAP_CACHE 0
34 #define USE_PIXMAP_CACHE 1
42 GuiPainter::GuiPainter(QPaintDevice * device)
43 : QPainter(device), Painter(),
44 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
46 // new QPainter has default QPen:
47 current_color_ = guiApp->colorCache().get(Color_black);
48 current_ls_ = line_solid;
49 current_lw_ = line_thin;
53 GuiPainter::~GuiPainter()
56 //lyxerr << "GuiPainter::end()" << endl;
60 void GuiPainter::setQPainterPen(QColor const & col,
61 Painter::line_style ls, Painter::line_width lw)
63 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
70 QPen pen = QPainter::pen();
74 case line_solid: pen.setStyle(Qt::SolidLine); break;
75 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
79 case line_thin: pen.setWidth(0); break;
80 case line_thick: pen.setWidth(3); break;
87 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
90 sig.append(QChar(static_cast<short>(f.family())));
91 sig.append(QChar(static_cast<short>(f.series())));
92 sig.append(QChar(static_cast<short>(f.realShape())));
93 sig.append(QChar(static_cast<short>(f.size())));
94 sig.append(QChar(static_cast<short>(f.color())));
95 if (!monochrome_min_.empty()) {
96 QColor const & min = monochrome_min_.top();
97 QColor const & max = monochrome_max_.top();
98 sig.append(QChar(static_cast<short>(min.red())));
99 sig.append(QChar(static_cast<short>(min.green())));
100 sig.append(QChar(static_cast<short>(min.blue())));
101 sig.append(QChar(static_cast<short>(max.red())));
102 sig.append(QChar(static_cast<short>(max.green())));
103 sig.append(QChar(static_cast<short>(max.blue())));
109 QColor GuiPainter::computeColor(ColorCode col)
111 return filterColor(guiApp->colorCache().get(col));
115 QColor GuiPainter::filterColor(QColor const & col)
117 if (monochrome_min_.empty())
120 // map into [min,max] interval
121 QColor const & min = monochrome_min_.top();
122 QColor const & max = monochrome_max_.top();
124 qreal v = col.valueF();
125 v *= v; // make it a bit steeper (i.e. darker)
127 qreal minr, ming, minb;
128 qreal maxr, maxg, maxb;
129 min.getRgbF(&minr, &ming, &minb);
130 max.getRgbF(&maxr, &maxg, &maxb);
134 v * (minr - maxr) + maxr,
135 v * (ming - maxg) + maxg,
136 v * (minb - maxb) + maxb);
141 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
143 QColor qmin = filterColor(guiApp->colorCache().get(min));
144 QColor qmax = filterColor(guiApp->colorCache().get(max));
145 monochrome_min_.push(qmin);
146 monochrome_max_.push(qmax);
150 void GuiPainter::leaveMonochromeMode()
152 BOOST_ASSERT(!monochrome_min_.empty());
153 monochrome_min_.pop();
154 monochrome_max_.pop();
158 void GuiPainter::point(int x, int y, ColorCode col)
160 if (!isDrawingEnabled())
163 setQPainterPen(computeColor(col));
168 void GuiPainter::line(int x1, int y1, int x2, int y2,
173 if (!isDrawingEnabled())
176 setQPainterPen(computeColor(col), ls, lw);
177 bool const do_antialiasing = renderHints() & TextAntialiasing
178 && x1 != x2 && y1 != y2;
179 setRenderHint(Antialiasing, do_antialiasing);
180 drawLine(x1, y1, x2, y2);
181 setRenderHint(Antialiasing, false);
185 void GuiPainter::lines(int const * xp, int const * yp, int np,
190 if (!isDrawingEnabled())
193 // double the size if needed
194 static QVector<QPoint> points(32);
195 if (np > points.size())
196 points.resize(2 * np);
198 bool antialias = false;
199 for (int i = 0; i < np; ++i) {
200 points[i].setX(xp[i]);
201 points[i].setY(yp[i]);
203 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
205 setQPainterPen(computeColor(col), ls, lw);
206 bool const text_is_antialiased = renderHints() & TextAntialiasing;
207 setRenderHint(Antialiasing, antialias && text_is_antialiased);
208 drawPolyline(points.data(), np);
209 setRenderHint(Antialiasing, false);
213 void GuiPainter::rectangle(int x, int y, int w, int h,
218 if (!isDrawingEnabled())
221 setQPainterPen(computeColor(col), ls, lw);
222 drawRect(x, y, w, h);
226 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
228 fillRect(x, y, w, h, guiApp->colorCache().get(col));
232 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
233 int a1, int a2, ColorCode col)
235 if (!isDrawingEnabled())
238 // LyX usings 1/64ths degree, Qt usings 1/16th
239 setQPainterPen(computeColor(col));
240 bool const do_antialiasing = renderHints() & TextAntialiasing;
241 setRenderHint(Antialiasing, do_antialiasing);
242 drawArc(x, y, w, h, a1 / 4, a2 / 4);
243 setRenderHint(Antialiasing, false);
247 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
249 graphics::GuiImage const & qlimage =
250 static_cast<graphics::GuiImage const &>(i);
252 fillRectangle(x, y, w, h, Color_graphicsbg);
254 if (!isDrawingEnabled())
257 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
261 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
264 return text(x, y, s, f);
268 int GuiPainter::smallCapsText(int x, int y,
269 QString const & s, FontInfo const & f)
271 FontInfo smallfont(f);
272 smallfont.decSize().decSize().setShape(UP_SHAPE);
274 QFont const & qfont = guiApp->guiFontLoader().get(f);
275 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
277 setQPainterPen(computeColor(f.realColor()));
279 size_t const ls = s.length();
280 for (unsigned int i = 0; i < ls; ++i) {
281 QChar const c = s[i].toUpper();
287 if (isDrawingEnabled())
288 drawText(x + textwidth, y, c);
289 textwidth += fontMetrics().width(c);
295 int GuiPainter::text(int x, int y, docstring const & s,
301 /* Caution: The following ucs4 to QString conversions work for symbol fonts
302 only because they are no real conversions but simple casts in reality.
303 When we want to draw a symbol or calculate the metrics we pass the position
304 of the symbol in the font (as given in lib/symbols) as a char_type to the
305 frontend. This is just wrong, because the symbol is no UCS4 character at
306 all. You can think of this number as the code point of the symbol in a
307 custom symbol encoding. It works because this char_type is lateron again
308 interpreted as a position in the font again.
309 The correct solution would be to have extra functions for symbols, but that
310 would require to duplicate a lot of frontend and mathed support code.
312 QString str = toqstr(s);
315 // HACK: QT3 refuses to show single compose characters
316 // Still needed with Qt4?
317 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
321 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
325 if (f.realShape() == SMALLCAPS_SHAPE) {
326 textwidth = smallCapsText(x, y, str, f);
327 if (f.underbar() == FONT_ON)
328 underline(f, x, y, textwidth);
332 // Here we use the font width cache instead of
333 // textwidth = fontMetrics().width(str);
334 // because the above is awfully expensive on MacOSX
335 textwidth = fi.metrics.width(s);
336 if (f.underbar() == FONT_ON)
337 underline(f, x, y, textwidth);
339 if (!isDrawingEnabled())
342 // Qt4 does not display a glyph whose codepoint is the
343 // same as that of a soft-hyphen (0x00ad), unless it
344 // occurs at a line-break. As a kludge, we force Qt to
345 // render this glyph using a one-column line.
346 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
347 setQPainterPen(computeColor(f.realColor()));
348 QTextLayout adsymbol(str);
349 adsymbol.setFont(fi.font);
350 adsymbol.beginLayout();
351 QTextLine line = adsymbol.createLine();
352 line.setNumColumns(1);
353 line.setPosition(QPointF(0, -line.ascent()));
354 adsymbol.endLayout();
355 line.draw(this, QPointF(x, y));
359 if (!use_pixmap_cache_) {
360 // don't use the pixmap cache,
361 // draw directly onto the painting device
362 setQPainterPen(computeColor(f.realColor()));
363 if (font() != fi.font)
365 // We need to draw the text as LTR as we use our own bidi code.
366 setLayoutDirection(Qt::LeftToRight);
367 // We need to draw the text as LTR as we use our own bidi code.
368 setLayoutDirection(Qt::LeftToRight);
370 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
371 // << " at " << x << "," << y);
376 QString key = generateStringSignature(str, f);
377 // Warning: Left bearing is in general negative! Only the case
378 // where left bearing is negative is of interest WRT the the
379 // pixmap width and the text x-position.
380 // Only the left bearing of the first character is important
381 // as we always write from left to right, even for
382 // right-to-left languages.
383 int const lb = min(fi.metrics.lbearing(s[0]), 0);
384 int const mA = fi.metrics.maxAscent();
385 if (!QPixmapCache::find(key, 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 = fi.metrics.rbearing(s[s.size()-1]);
390 int const w = textwidth + rb - lb;
391 int const mD = fi.metrics.maxDescent();
392 int const h = mA + mD;
394 pm.fill(Qt::transparent);
396 p.setQPainterPen(computeColor(f.realColor()));
397 if (p.font() != fi.font)
399 // We need to draw the text as LTR as we use our own bidi code.
400 p.setLayoutDirection(Qt::LeftToRight);
401 p.drawText(-lb, mA, str);
402 QPixmapCache::insert(key, pm);
403 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
404 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
407 // Draw the cached pixmap.
408 drawPixmap(x + lb, y - mA, pm);
414 static int max(int a, int b) { return a > b ? a : b; }
417 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
420 fillRectangle(x, y, w, h, Color_buttonhoverbg);
422 fillRectangle(x, y, w, h, Color_buttonbg);
423 buttonFrame(x, y, w, h);
427 void GuiPainter::buttonFrame(int x, int y, int w, int h)
429 line(x, y, x, y + h - 1, Color_buttonframe);
430 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
431 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
432 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
435 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
437 void GuiPainter::rectText(int x, int y, docstring const & str,
438 FontInfo const & font, ColorCode back, ColorCode frame)
444 FontMetrics const & fm = theFontMetrics(font);
445 fm.rectText(str, width, ascent, descent);
447 if (back != Color_none)
448 fillRectangle(x + 1, y - ascent + 1, width - 1,
449 ascent + descent - 1, back);
451 if (frame != Color_none)
452 rectangle(x, y - ascent, width, ascent + descent, frame);
454 text(x + 3, y, str, font);
458 void GuiPainter::buttonText(int x, int y, docstring const & str,
459 FontInfo const & font, bool mouseHover)
465 FontMetrics const & fm = theFontMetrics(font);
466 fm.buttonText(str, width, ascent, descent);
468 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
469 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
473 int GuiPainter::preeditText(int x, int y, char_type c,
474 FontInfo const & font, preedit_style style)
476 FontInfo temp_font = font;
477 FontMetrics const & fm = theFontMetrics(font);
478 int ascent = fm.maxAscent();
479 int descent = fm.maxDescent();
480 int height = ascent + descent;
481 int width = fm.width(c);
484 case preedit_default:
485 // default unselecting mode.
486 fillRectangle(x, y - height + 1, width, height, Color_background);
487 dashedUnderline(font, x, y - descent + 1, width);
489 case preedit_selecting:
490 // We are in selecting mode: white text on black background.
491 fillRectangle(x, y - height + 1, width, height, Color_black);
492 temp_font.setColor(Color_white);
495 // The character comes with a cursor.
496 fillRectangle(x, y - height + 1, width, height, Color_background);
497 underline(font, x, y - descent + 1, width);
500 text(x, y - descent + 1, c, temp_font);
506 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
508 FontMetrics const & fm = theFontMetrics(f);
510 int const below = max(fm.maxDescent() / 2, 2);
511 int const height = max((fm.maxDescent() / 4) - 1, 1);
514 line(x, y + below, x + width, y + below, f.color());
516 fillRectangle(x, y + below, width, below + height, f.color());
520 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
522 FontMetrics const & fm = theFontMetrics(f);
524 int const below = max(fm.maxDescent() / 2, 2);
525 int height = max((fm.maxDescent() / 4) - 1, 1);
530 for (int n = 0; n != height; ++n)
531 line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
534 } // namespace frontend