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 GuiPainter::GuiPainter(QPaintDevice * device)
49 : QPainter(device), Painter(),
50 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
52 // new QPainter has default QPen:
53 current_color_ = guiApp->colorCache().get(Color_black);
54 current_ls_ = line_solid;
55 current_lw_ = line_thin;
59 GuiPainter::~GuiPainter()
62 //lyxerr << "GuiPainter::end()" << endl;
66 void GuiPainter::setQPainterPen(QColor const & col,
67 Painter::line_style ls, Painter::line_width lw)
69 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
76 QPen pen = QPainter::pen();
80 case line_solid: pen.setStyle(Qt::SolidLine); break;
81 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
85 case line_thin: pen.setWidth(0); break;
86 case line_thick: pen.setWidth(3); break;
93 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
96 sig.append(QChar(static_cast<short>(f.family())));
97 sig.append(QChar(static_cast<short>(f.series())));
98 sig.append(QChar(static_cast<short>(f.realShape())));
99 sig.append(QChar(static_cast<short>(f.size())));
100 sig.append(QChar(static_cast<short>(f.color())));
101 if (!monochrome_min_.empty()) {
102 QColor const & min = monochrome_min_.top();
103 QColor const & max = monochrome_max_.top();
104 sig.append(QChar(static_cast<short>(min.red())));
105 sig.append(QChar(static_cast<short>(min.green())));
106 sig.append(QChar(static_cast<short>(min.blue())));
107 sig.append(QChar(static_cast<short>(max.red())));
108 sig.append(QChar(static_cast<short>(max.green())));
109 sig.append(QChar(static_cast<short>(max.blue())));
115 QColor GuiPainter::computeColor(ColorCode col)
117 return filterColor(guiApp->colorCache().get(col));
121 QColor GuiPainter::filterColor(QColor const & col)
123 if (monochrome_min_.empty())
126 // map into [min,max] interval
127 QColor const & min = monochrome_min_.top();
128 QColor const & max = monochrome_max_.top();
130 qreal v = col.valueF();
131 v *= v; // make it a bit steeper (i.e. darker)
133 qreal minr, ming, minb;
134 qreal maxr, maxg, maxb;
135 min.getRgbF(&minr, &ming, &minb);
136 max.getRgbF(&maxr, &maxg, &maxb);
140 v * (minr - maxr) + maxr,
141 v * (ming - maxg) + maxg,
142 v * (minb - maxb) + maxb);
147 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
149 QColor qmin = filterColor(guiApp->colorCache().get(min));
150 QColor qmax = filterColor(guiApp->colorCache().get(max));
151 monochrome_min_.push(qmin);
152 monochrome_max_.push(qmax);
156 void GuiPainter::leaveMonochromeMode()
158 LASSERT(!monochrome_min_.empty(), /**/);
159 monochrome_min_.pop();
160 monochrome_max_.pop();
164 void GuiPainter::point(int x, int y, ColorCode col)
166 if (!isDrawingEnabled())
169 setQPainterPen(computeColor(col));
174 void GuiPainter::line(int x1, int y1, int x2, int y2,
179 if (!isDrawingEnabled())
182 setQPainterPen(computeColor(col), ls, lw);
183 bool const do_antialiasing = renderHints() & TextAntialiasing
184 && x1 != x2 && y1 != y2;
185 setRenderHint(Antialiasing, do_antialiasing);
186 drawLine(x1, y1, x2, y2);
187 setRenderHint(Antialiasing, false);
191 void GuiPainter::lines(int const * xp, int const * yp, int np,
196 if (!isDrawingEnabled())
199 // double the size if needed
200 static QVector<QPoint> points(32);
201 if (np > points.size())
202 points.resize(2 * np);
204 bool antialias = false;
205 for (int i = 0; i < np; ++i) {
206 points[i].setX(xp[i]);
207 points[i].setY(yp[i]);
209 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
211 setQPainterPen(computeColor(col), ls, lw);
212 bool const text_is_antialiased = renderHints() & TextAntialiasing;
213 setRenderHint(Antialiasing, antialias && text_is_antialiased);
214 drawPolyline(points.data(), np);
215 setRenderHint(Antialiasing, false);
219 void GuiPainter::rectangle(int x, int y, int w, int h,
224 if (!isDrawingEnabled())
227 setQPainterPen(computeColor(col), ls, lw);
228 drawRect(x, y, w, h);
232 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
234 fillRect(x, y, w, h, guiApp->colorCache().get(col));
238 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
239 int a1, int a2, ColorCode col)
241 if (!isDrawingEnabled())
244 // LyX usings 1/64ths degree, Qt usings 1/16th
245 setQPainterPen(computeColor(col));
246 bool const do_antialiasing = renderHints() & TextAntialiasing;
247 setRenderHint(Antialiasing, do_antialiasing);
248 drawArc(x, y, w, h, a1 / 4, a2 / 4);
249 setRenderHint(Antialiasing, false);
253 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
255 graphics::GuiImage const & qlimage =
256 static_cast<graphics::GuiImage const &>(i);
258 fillRectangle(x, y, w, h, Color_graphicsbg);
260 if (!isDrawingEnabled())
263 drawPixmap(x, y, qlimage.pixmap(), 0, 0, w, h);
267 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
270 return text(x, y, s, f);
274 int GuiPainter::smallCapsText(int x, int y,
275 QString const & s, FontInfo const & f)
277 FontInfo smallfont(f);
278 smallfont.decSize().decSize().setShape(UP_SHAPE);
280 QFont const & qfont = getFont(f);
281 QFont const & qsmallfont = getFont(smallfont);
283 setQPainterPen(computeColor(f.realColor()));
285 size_t const ls = s.length();
286 for (unsigned int i = 0; i < ls; ++i) {
287 QChar const c = s[i].toUpper();
293 if (isDrawingEnabled())
294 drawText(x + textwidth, y, c);
295 textwidth += fontMetrics().width(c);
301 int GuiPainter::text(int x, int y, docstring const & s,
307 /* Caution: The following ucs4 to QString conversions work for symbol fonts
308 only because they are no real conversions but simple casts in reality.
309 When we want to draw a symbol or calculate the metrics we pass the position
310 of the symbol in the font (as given in lib/symbols) as a char_type to the
311 frontend. This is just wrong, because the symbol is no UCS4 character at
312 all. You can think of this number as the code point of the symbol in a
313 custom symbol encoding. It works because this char_type is lateron again
314 interpreted as a position in the font again.
315 The correct solution would be to have extra functions for symbols, but that
316 would require to duplicate a lot of frontend and mathed support code.
318 QString str = toqstr(s);
321 // HACK: QT3 refuses to show single compose characters
322 // Still needed with Qt4?
323 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
327 QFont const & ff = getFont(f);
328 GuiFontMetrics const & fm = getFontMetrics(f);
332 if (f.realShape() == SMALLCAPS_SHAPE) {
333 textwidth = smallCapsText(x, y, str, f);
334 if (f.underbar() == FONT_ON)
335 underline(f, x, y, textwidth);
339 // Here we use the font width cache instead of
340 // textwidth = fontMetrics().width(str);
341 // because the above is awfully expensive on MacOSX
342 textwidth = fm.width(s);
343 if (f.underbar() == FONT_ON)
344 underline(f, x, y, textwidth);
346 if (!isDrawingEnabled())
349 // Qt4 does not display a glyph whose codepoint is the
350 // same as that of a soft-hyphen (0x00ad), unless it
351 // occurs at a line-break. As a kludge, we force Qt to
352 // render this glyph using a one-column line.
353 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
354 setQPainterPen(computeColor(f.realColor()));
355 QTextLayout adsymbol(str);
356 adsymbol.setFont(ff);
357 adsymbol.beginLayout();
358 QTextLine line = adsymbol.createLine();
359 line.setNumColumns(1);
360 line.setPosition(QPointF(0, -line.ascent()));
361 adsymbol.endLayout();
362 line.draw(this, QPointF(x, y));
366 if (!use_pixmap_cache_) {
367 // don't use the pixmap cache,
368 // draw directly onto the painting device
369 setQPainterPen(computeColor(f.realColor()));
372 // We need to draw the text as LTR as we use our own bidi code.
373 setLayoutDirection(Qt::LeftToRight);
375 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
376 // << " at " << x << "," << y);
381 QString key = generateStringSignature(str, f);
382 // Warning: Left bearing is in general negative! Only the case
383 // where left bearing is negative is of interest WRT the the
384 // pixmap width and the text x-position.
385 // Only the left bearing of the first character is important
386 // as we always write from left to right, even for
387 // right-to-left languages.
388 int const lb = min(fm.lbearing(s[0]), 0);
389 int const mA = fm.maxAscent();
390 if (!QPixmapCache::find(key, pm)) {
391 // Only the right bearing of the last character is
392 // important as we always write from left to right,
393 // even for right-to-left languages.
394 int const rb = fm.rbearing(s[s.size()-1]);
395 int const w = textwidth + rb - lb;
396 int const mD = fm.maxDescent();
397 int const h = mA + mD;
399 pm.fill(Qt::transparent);
401 p.setQPainterPen(computeColor(f.realColor()));
404 // We need to draw the text as LTR as we use our own bidi code.
405 p.setLayoutDirection(Qt::LeftToRight);
406 p.drawText(-lb, mA, str);
407 QPixmapCache::insert(key, pm);
408 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
409 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
412 // Draw the cached pixmap.
413 drawPixmap(x + lb, y - mA, pm);
419 static int max(int a, int b) { return a > b ? a : b; }
422 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
425 fillRectangle(x, y, w, h, Color_buttonhoverbg);
427 fillRectangle(x, y, w, h, Color_buttonbg);
428 buttonFrame(x, y, w, h);
432 void GuiPainter::buttonFrame(int x, int y, int w, int h)
434 line(x, y, x, y + h - 1, Color_buttonframe);
435 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
436 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
437 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
441 void GuiPainter::rectText(int x, int y, docstring const & str,
442 FontInfo const & font, ColorCode back, ColorCode frame)
448 FontMetrics const & fm = theFontMetrics(font);
449 fm.rectText(str, width, ascent, descent);
451 if (back != Color_none)
452 fillRectangle(x + 1, y - ascent + 1, width - 1,
453 ascent + descent - 1, back);
455 if (frame != Color_none)
456 rectangle(x, y - ascent, width, ascent + descent, frame);
458 text(x + 3, y, str, font);
462 void GuiPainter::buttonText(int x, int y, docstring const & str,
463 FontInfo const & font, bool mouseHover)
469 FontMetrics const & fm = theFontMetrics(font);
470 fm.buttonText(str, width, ascent, descent);
472 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
474 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
475 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
479 int GuiPainter::preeditText(int x, int y, char_type c,
480 FontInfo const & font, preedit_style style)
482 FontInfo temp_font = font;
483 FontMetrics const & fm = theFontMetrics(font);
484 int ascent = fm.maxAscent();
485 int descent = fm.maxDescent();
486 int height = ascent + descent;
487 int width = fm.width(c);
490 case preedit_default:
491 // default unselecting mode.
492 fillRectangle(x, y - height + 1, width, height, Color_background);
493 dashedUnderline(font, x, y - descent + 1, width);
495 case preedit_selecting:
496 // We are in selecting mode: white text on black background.
497 fillRectangle(x, y - height + 1, width, height, Color_black);
498 temp_font.setColor(Color_white);
501 // The character comes with a cursor.
502 fillRectangle(x, y - height + 1, width, height, Color_background);
503 underline(font, x, y - descent + 1, width);
506 text(x, y - descent + 1, c, temp_font);
512 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
514 FontMetrics const & fm = theFontMetrics(f);
516 int const below = max(fm.maxDescent() / 2, 2);
517 int const height = max((fm.maxDescent() / 4) - 1, 1);
520 line(x, y + below, x + width, y + below, f.color());
522 fillRectangle(x, y + below, width, below + height, f.color());
526 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
528 FontMetrics const & fm = theFontMetrics(f);
530 int const below = max(fm.maxDescent() / 2, 2);
531 int height = max((fm.maxDescent() / 4) - 1, 1);
536 for (int n = 0; n != height; ++n)
537 line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
540 } // namespace frontend