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 if (!isDrawingEnabled())
237 fillRect(x, y, w, h, guiApp->colorCache().get(col));
241 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
242 int a1, int a2, ColorCode col)
244 if (!isDrawingEnabled())
247 // LyX usings 1/64ths degree, Qt usings 1/16th
248 setQPainterPen(computeColor(col));
249 bool const do_antialiasing = renderHints() & TextAntialiasing;
250 setRenderHint(Antialiasing, do_antialiasing);
251 drawArc(x, y, w, h, a1 / 4, a2 / 4);
252 setRenderHint(Antialiasing, false);
256 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
258 graphics::GuiImage const & qlimage =
259 static_cast<graphics::GuiImage const &>(i);
261 fillRectangle(x, y, w, h, Color_graphicsbg);
263 if (!isDrawingEnabled())
266 drawImage(x, y, qlimage.image(), 0, 0, w, h);
270 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
273 return text(x, y, s, f);
277 int GuiPainter::smallCapsText(int x, int y,
278 QString const & s, FontInfo const & f)
280 FontInfo smallfont(f);
281 smallfont.decSize().decSize().setShape(UP_SHAPE);
283 QFont const & qfont = getFont(f);
284 QFont const & qsmallfont = getFont(smallfont);
286 setQPainterPen(computeColor(f.realColor()));
288 size_t const ls = s.length();
289 for (unsigned int i = 0; i < ls; ++i) {
290 QChar const c = s[i].toUpper();
296 if (isDrawingEnabled())
297 drawText(x + textwidth, y, c);
298 textwidth += fontMetrics().width(c);
304 int GuiPainter::text(int x, int y, docstring const & s,
310 /* Caution: The following ucs4 to QString conversions work for symbol fonts
311 only because they are no real conversions but simple casts in reality.
312 When we want to draw a symbol or calculate the metrics we pass the position
313 of the symbol in the font (as given in lib/symbols) as a char_type to the
314 frontend. This is just wrong, because the symbol is no UCS4 character at
315 all. You can think of this number as the code point of the symbol in a
316 custom symbol encoding. It works because this char_type is lateron again
317 interpreted as a position in the font again.
318 The correct solution would be to have extra functions for symbols, but that
319 would require to duplicate a lot of frontend and mathed support code.
321 QString str = toqstr(s);
324 // HACK: QT3 refuses to show single compose characters
325 // Still needed with Qt4?
326 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
330 QFont const & ff = getFont(f);
331 GuiFontMetrics const & fm = getFontMetrics(f);
335 if (f.realShape() == SMALLCAPS_SHAPE) {
336 textwidth = smallCapsText(x, y, str, f);
337 if (f.underbar() == FONT_ON)
338 underline(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 if (f.underbar() == FONT_ON)
347 underline(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 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
357 setQPainterPen(computeColor(f.realColor()));
358 QTextLayout adsymbol(str);
359 adsymbol.setFont(ff);
360 adsymbol.beginLayout();
361 QTextLine line = adsymbol.createLine();
362 line.setNumColumns(1);
363 line.setPosition(QPointF(0, -line.ascent()));
364 adsymbol.endLayout();
365 line.draw(this, QPointF(x, y));
369 if (use_pixmap_cache_) {
371 QString key = generateStringSignature(str, f);
373 // Warning: Left bearing is in general negative! Only the case
374 // where left bearing is negative is of interest WRT the
375 // pixmap width and the text x-position.
376 // Only the left bearing of the first character is important
377 // as we always write from left to right, even for
378 // right-to-left languages.
379 int const lb = min(fm.lbearing(s[0]), 0);
380 int const mA = fm.maxAscent();
381 if (QPixmapCache::find(key, pm)) {
382 // Draw the cached pixmap.
383 drawPixmap(x + lb, y - mA, 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 = fm.rbearing(s[s.size()-1]);
391 int const w = textwidth + rb - lb;
392 int const mD = fm.maxDescent();
393 int const h = mA + mD;
394 if (w > 0 && h > 0) {
396 pm.fill(Qt::transparent);
398 p.setQPainterPen(computeColor(f.realColor()));
401 // We need to draw the text as LTR as we use our own bidi code.
402 p.setLayoutDirection(Qt::LeftToRight);
403 p.drawText(-lb, mA, str);
404 QPixmapCache::insert(key, pm);
405 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
406 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
412 // don't use the pixmap cache,
413 // draw directly onto the painting device
414 setQPainterPen(computeColor(f.realColor()));
417 // We need to draw the text as LTR as we use our own bidi code.
418 QPainter::setLayoutDirection(Qt::LeftToRight);
420 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
421 // << " at " << x << "," << y);
426 static int max(int a, int b) { return a > b ? a : b; }
429 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
432 fillRectangle(x, y, w, h, Color_buttonhoverbg);
434 fillRectangle(x, y, w, h, Color_buttonbg);
435 buttonFrame(x, y, w, h);
439 void GuiPainter::buttonFrame(int x, int y, int w, int h)
441 line(x, y, x, y + h - 1, Color_buttonframe);
442 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
443 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
444 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
448 void GuiPainter::rectText(int x, int y, docstring const & str,
449 FontInfo const & font, ColorCode back, ColorCode frame)
455 FontMetrics const & fm = theFontMetrics(font);
456 fm.rectText(str, width, ascent, descent);
458 if (back != Color_none)
459 fillRectangle(x + 1, y - ascent + 1, width - 1,
460 ascent + descent - 1, back);
462 if (frame != Color_none)
463 rectangle(x, y - ascent, width, ascent + descent, frame);
465 text(x + 3, y, str, font);
469 void GuiPainter::buttonText(int x, int y, docstring const & str,
470 FontInfo const & font, bool mouseHover)
476 FontMetrics const & fm = theFontMetrics(font);
477 fm.buttonText(str, width, ascent, descent);
479 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
481 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
482 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
486 int GuiPainter::preeditText(int x, int y, char_type c,
487 FontInfo const & font, preedit_style style)
489 FontInfo temp_font = font;
490 FontMetrics const & fm = theFontMetrics(font);
491 int ascent = fm.maxAscent();
492 int descent = fm.maxDescent();
493 int height = ascent + descent;
494 int width = fm.width(c);
497 case preedit_default:
498 // default unselecting mode.
499 fillRectangle(x, y - height + 1, width, height, Color_background);
500 dashedUnderline(font, x, y - descent + 1, width);
502 case preedit_selecting:
503 // We are in selecting mode: white text on black background.
504 fillRectangle(x, y - height + 1, width, height, Color_black);
505 temp_font.setColor(Color_white);
508 // The character comes with a cursor.
509 fillRectangle(x, y - height + 1, width, height, Color_background);
510 underline(font, x, y - descent + 1, width);
513 text(x, y - descent + 1, c, temp_font);
519 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
521 FontMetrics const & fm = theFontMetrics(f);
523 int const below = max(fm.maxDescent() / 2, 2);
524 int const height = max((fm.maxDescent() / 4) - 1, 1);
527 line(x, y + below, x + width, y + below, f.realColor());
529 fillRectangle(x, y + below, width, below + height, f.realColor());
533 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
535 FontMetrics const & fm = theFontMetrics(f);
537 int const below = max(fm.maxDescent() / 2, 2);
538 int height = max((fm.maxDescent() / 4) - 1, 1);
543 for (int n = 0; n != height; ++n)
544 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
547 } // namespace frontend