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"
20 #include "GuiApplication.h"
21 #include "qt_helpers.h"
23 #include "support/debug.h"
27 #include <QPixmapCache>
28 #include <QTextLayout>
30 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
31 // drawing text. This is especially useful for older PPC/Mac systems.
33 #define USE_PIXMAP_CACHE 0
35 #define USE_PIXMAP_CACHE 1
43 GuiPainter::GuiPainter(QPaintDevice * device)
44 : QPainter(device), Painter(),
45 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
47 // new QPainter has default QPen:
48 current_color_ = guiApp->colorCache().get(Color_black);
49 current_ls_ = line_solid;
50 current_lw_ = line_thin;
54 GuiPainter::~GuiPainter()
57 //lyxerr << "GuiPainter::end()" << endl;
61 void GuiPainter::setQPainterPen(QColor const & col,
62 Painter::line_style ls, Painter::line_width lw)
64 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
71 QPen pen = QPainter::pen();
75 case line_solid: pen.setStyle(Qt::SolidLine); break;
76 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
80 case line_thin: pen.setWidth(0); break;
81 case line_thick: pen.setWidth(3); break;
88 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
91 sig.append(QChar(static_cast<short>(f.family())));
92 sig.append(QChar(static_cast<short>(f.series())));
93 sig.append(QChar(static_cast<short>(f.realShape())));
94 sig.append(QChar(static_cast<short>(f.size())));
95 sig.append(QChar(static_cast<short>(f.color())));
96 if (!monochrome_min_.empty()) {
97 QColor const & min = monochrome_min_.top();
98 QColor const & max = monochrome_max_.top();
99 sig.append(QChar(static_cast<short>(min.red())));
100 sig.append(QChar(static_cast<short>(min.green())));
101 sig.append(QChar(static_cast<short>(min.blue())));
102 sig.append(QChar(static_cast<short>(max.red())));
103 sig.append(QChar(static_cast<short>(max.green())));
104 sig.append(QChar(static_cast<short>(max.blue())));
110 QColor GuiPainter::computeColor(ColorCode col)
112 return filterColor(guiApp->colorCache().get(col));
116 QColor GuiPainter::filterColor(QColor const & col)
118 if (monochrome_min_.empty())
121 // map into [min,max] interval
122 QColor const & min = monochrome_min_.top();
123 QColor const & max = monochrome_max_.top();
125 qreal v = col.valueF();
126 v *= v; // make it a bit steeper (i.e. darker)
128 qreal minr, ming, minb;
129 qreal maxr, maxg, maxb;
130 min.getRgbF(&minr, &ming, &minb);
131 max.getRgbF(&maxr, &maxg, &maxb);
135 v * (minr - maxr) + maxr,
136 v * (ming - maxg) + maxg,
137 v * (minb - maxb) + maxb);
142 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
144 QColor qmin = filterColor(guiApp->colorCache().get(min));
145 QColor qmax = filterColor(guiApp->colorCache().get(max));
146 monochrome_min_.push(qmin);
147 monochrome_max_.push(qmax);
151 void GuiPainter::leaveMonochromeMode()
153 BOOST_ASSERT(!monochrome_min_.empty());
154 monochrome_min_.pop();
155 monochrome_max_.pop();
159 void GuiPainter::point(int x, int y, ColorCode col)
161 if (!isDrawingEnabled())
164 setQPainterPen(computeColor(col));
169 void GuiPainter::line(int x1, int y1, int x2, int y2,
174 if (!isDrawingEnabled())
177 setQPainterPen(computeColor(col), ls, lw);
178 bool const do_antialiasing = renderHints() & TextAntialiasing
179 && x1 != x2 && y1 != y2;
180 setRenderHint(Antialiasing, do_antialiasing);
181 drawLine(x1, y1, x2, y2);
182 setRenderHint(Antialiasing, false);
186 void GuiPainter::lines(int const * xp, int const * yp, int np,
191 if (!isDrawingEnabled())
194 // double the size if needed
195 static QVector<QPoint> points(32);
196 if (np > points.size())
197 points.resize(2 * np);
199 bool antialias = false;
200 for (int i = 0; i < np; ++i) {
201 points[i].setX(xp[i]);
202 points[i].setY(yp[i]);
204 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
206 setQPainterPen(computeColor(col), ls, lw);
207 bool const text_is_antialiased = renderHints() & TextAntialiasing;
208 setRenderHint(Antialiasing, antialias && text_is_antialiased);
209 drawPolyline(points.data(), np);
210 setRenderHint(Antialiasing, false);
214 void GuiPainter::rectangle(int x, int y, int w, int h,
219 if (!isDrawingEnabled())
222 setQPainterPen(computeColor(col), ls, lw);
223 drawRect(x, y, w, h);
227 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
229 fillRect(x, y, w, h, guiApp->colorCache().get(col));
233 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
234 int a1, int a2, ColorCode col)
236 if (!isDrawingEnabled())
239 // LyX usings 1/64ths degree, Qt usings 1/16th
240 setQPainterPen(computeColor(col));
241 bool const do_antialiasing = renderHints() & TextAntialiasing;
242 setRenderHint(Antialiasing, do_antialiasing);
243 drawArc(x, y, w, h, a1 / 4, a2 / 4);
244 setRenderHint(Antialiasing, false);
248 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
250 graphics::GuiImage const & qlimage =
251 static_cast<graphics::GuiImage const &>(i);
253 fillRectangle(x, y, w, h, Color_graphicsbg);
255 if (!isDrawingEnabled())
258 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
262 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
265 return text(x, y, s, f);
269 int GuiPainter::smallCapsText(int x, int y,
270 QString const & s, FontInfo const & f)
272 FontInfo smallfont(f);
273 smallfont.decSize().decSize().setShape(UP_SHAPE);
275 QFont const & qfont = guiApp->guiFontLoader().get(f);
276 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
278 setQPainterPen(computeColor(f.realColor()));
280 size_t const ls = s.length();
281 for (unsigned int i = 0; i < ls; ++i) {
282 QChar const c = s[i].toUpper();
288 if (isDrawingEnabled())
289 drawText(x + textwidth, y, c);
290 textwidth += fontMetrics().width(c);
296 int GuiPainter::text(int x, int y, docstring const & s,
302 /* Caution: The following ucs4 to QString conversions work for symbol fonts
303 only because they are no real conversions but simple casts in reality.
304 When we want to draw a symbol or calculate the metrics we pass the position
305 of the symbol in the font (as given in lib/symbols) as a char_type to the
306 frontend. This is just wrong, because the symbol is no UCS4 character at
307 all. You can think of this number as the code point of the symbol in a
308 custom symbol encoding. It works because this char_type is lateron again
309 interpreted as a position in the font again.
310 The correct solution would be to have extra functions for symbols, but that
311 would require to duplicate a lot of frontend and mathed support code.
313 QString str = toqstr(s);
316 // HACK: QT3 refuses to show single compose characters
317 // Still needed with Qt4?
318 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
322 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
326 if (f.realShape() == SMALLCAPS_SHAPE) {
327 textwidth = smallCapsText(x, y, str, f);
328 if (f.underbar() == FONT_ON)
329 underline(f, x, y, textwidth);
333 // Here we use the font width cache instead of
334 // textwidth = fontMetrics().width(str);
335 // because the above is awfully expensive on MacOSX
336 textwidth = fi.metrics.width(s);
337 if (f.underbar() == FONT_ON)
338 underline(f, x, y, textwidth);
340 if (!isDrawingEnabled())
343 // Qt4 does not display a glyph whose codepoint is the
344 // same as that of a soft-hyphen (0x00ad), unless it
345 // occurs at a line-break. As a kludge, we force Qt to
346 // render this glyph using a one-column line.
347 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
348 setQPainterPen(computeColor(f.realColor()));
349 QTextLayout adsymbol(str);
350 adsymbol.setFont(fi.font);
351 adsymbol.beginLayout();
352 QTextLine line = adsymbol.createLine();
353 line.setNumColumns(1);
354 line.setPosition(QPointF(0, -line.ascent()));
355 adsymbol.endLayout();
356 line.draw(this, QPointF(x, y));
360 if (!use_pixmap_cache_) {
361 // don't use the pixmap cache,
362 // draw directly onto the painting device
363 setQPainterPen(computeColor(f.realColor()));
364 if (font() != fi.font)
366 // We need to draw the text as LTR as we use our own bidi code.
367 setLayoutDirection(Qt::LeftToRight);
368 // We need to draw the text as LTR as we use our own bidi code.
369 setLayoutDirection(Qt::LeftToRight);
371 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
372 // << " at " << x << "," << y);
377 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 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(fi.metrics.lbearing(s[0]), 0);
385 int const mA = fi.metrics.maxAscent();
386 if (!QPixmapCache::find(key, pm)) {
387 // Only the right bearing of the last character is
388 // important as we always write from left to right,
389 // even for right-to-left languages.
390 int const rb = fi.metrics.rbearing(s[s.size()-1]);
391 int const w = textwidth + rb - lb;
392 int const mD = fi.metrics.maxDescent();
393 int const h = mA + mD;
395 pm.fill(Qt::transparent);
397 p.setQPainterPen(computeColor(f.realColor()));
398 if (p.font() != fi.font)
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 cached pixmap.
409 drawPixmap(x + lb, y - mA, pm);
415 static int max(int a, int b) { return a > b ? a : b; }
418 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
421 fillRectangle(x, y, w, h, Color_buttonhoverbg);
423 fillRectangle(x, y, w, h, Color_buttonbg);
424 buttonFrame(x, y, w, h);
428 void GuiPainter::buttonFrame(int x, int y, int w, int h)
430 line(x, y, x, y + h - 1, Color_buttonframe);
431 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
432 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
433 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
436 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
438 void GuiPainter::rectText(int x, int y, docstring const & str,
439 FontInfo const & font, ColorCode back, ColorCode frame)
445 FontMetrics const & fm = theFontMetrics(font);
446 fm.rectText(str, width, ascent, descent);
448 if (back != Color_none)
449 fillRectangle(x + 1, y - ascent + 1, width - 1,
450 ascent + descent - 1, back);
452 if (frame != Color_none)
453 rectangle(x, y - ascent, width, ascent + descent, frame);
455 text(x + 3, y, str, font);
459 void GuiPainter::buttonText(int x, int y, docstring const & str,
460 FontInfo const & font, bool mouseHover)
466 FontMetrics const & fm = theFontMetrics(font);
467 fm.buttonText(str, width, ascent, descent);
469 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
470 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
474 int GuiPainter::preeditText(int x, int y, char_type c,
475 FontInfo const & font, preedit_style style)
477 FontInfo temp_font = font;
478 FontMetrics const & fm = theFontMetrics(font);
479 int ascent = fm.maxAscent();
480 int descent = fm.maxDescent();
481 int height = ascent + descent;
482 int width = fm.width(c);
485 case preedit_default:
486 // default unselecting mode.
487 fillRectangle(x, y - height + 1, width, height, Color_background);
488 dashedUnderline(font, x, y - descent + 1, width);
490 case preedit_selecting:
491 // We are in selecting mode: white text on black background.
492 fillRectangle(x, y - height + 1, width, height, Color_black);
493 temp_font.setColor(Color_white);
496 // The character comes with a cursor.
497 fillRectangle(x, y - height + 1, width, height, Color_background);
498 underline(font, x, y - descent + 1, width);
501 text(x, y - descent + 1, c, temp_font);
507 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
509 FontMetrics const & fm = theFontMetrics(f);
511 int const below = max(fm.maxDescent() / 2, 2);
512 int const height = max((fm.maxDescent() / 4) - 1, 1);
515 line(x, y + below, x + width, y + below, f.color());
517 fillRectangle(x, y + below, width, below + height, f.color());
521 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
523 FontMetrics const & fm = theFontMetrics(f);
525 int const below = max(fm.maxDescent() / 2, 2);
526 int height = max((fm.maxDescent() / 4) - 1, 1);
531 for (int n = 0; n != height; ++n)
532 line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
535 } // namespace frontend