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 "support/unicode.h"
29 #include <QPixmapCache>
30 #include <QTextLayout>
32 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
33 // drawing text. This is especially useful for older PPC/Mac systems.
35 #define USE_PIXMAP_CACHE 0
37 #define USE_PIXMAP_CACHE 1
45 GuiPainter::GuiPainter(QPaintDevice * device)
46 : QPainter(device), Painter(),
47 use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
49 // new QPainter has default QPen:
50 current_color_ = guiApp->colorCache().get(Color_black);
51 current_ls_ = line_solid;
52 current_lw_ = line_thin;
56 GuiPainter::~GuiPainter()
59 //lyxerr << "GuiPainter::end()" << endl;
63 void GuiPainter::setQPainterPen(QColor const & col,
64 Painter::line_style ls, Painter::line_width lw)
66 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
73 QPen pen = QPainter::pen();
77 case line_solid: pen.setStyle(Qt::SolidLine); break;
78 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
82 case line_thin: pen.setWidth(0); break;
83 case line_thick: pen.setWidth(3); break;
90 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
93 sig.append(QChar(static_cast<short>(f.family())));
94 sig.append(QChar(static_cast<short>(f.series())));
95 sig.append(QChar(static_cast<short>(f.realShape())));
96 sig.append(QChar(static_cast<short>(f.size())));
97 sig.append(QChar(static_cast<short>(f.color())));
98 if (!monochrome_min_.empty()) {
99 QColor const & min = monochrome_min_.top();
100 QColor const & max = monochrome_max_.top();
101 sig.append(QChar(static_cast<short>(min.red())));
102 sig.append(QChar(static_cast<short>(min.green())));
103 sig.append(QChar(static_cast<short>(min.blue())));
104 sig.append(QChar(static_cast<short>(max.red())));
105 sig.append(QChar(static_cast<short>(max.green())));
106 sig.append(QChar(static_cast<short>(max.blue())));
112 QColor GuiPainter::computeColor(ColorCode col)
114 return filterColor(guiApp->colorCache().get(col));
118 QColor GuiPainter::filterColor(QColor const & col)
120 if (monochrome_min_.empty())
123 // map into [min,max] interval
124 QColor const & min = monochrome_min_.top();
125 QColor const & max = monochrome_max_.top();
127 qreal v = col.valueF();
128 v *= v; // make it a bit steeper (i.e. darker)
130 qreal minr, ming, minb;
131 qreal maxr, maxg, maxb;
132 min.getRgbF(&minr, &ming, &minb);
133 max.getRgbF(&maxr, &maxg, &maxb);
137 v * (minr - maxr) + maxr,
138 v * (ming - maxg) + maxg,
139 v * (minb - maxb) + maxb);
144 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
146 QColor qmin = filterColor(guiApp->colorCache().get(min));
147 QColor qmax = filterColor(guiApp->colorCache().get(max));
148 monochrome_min_.push(qmin);
149 monochrome_max_.push(qmax);
153 void GuiPainter::leaveMonochromeMode()
155 BOOST_ASSERT(!monochrome_min_.empty());
156 monochrome_min_.pop();
157 monochrome_max_.pop();
161 void GuiPainter::point(int x, int y, ColorCode col)
163 if (!isDrawingEnabled())
166 setQPainterPen(computeColor(col));
171 void GuiPainter::line(int x1, int y1, int x2, int y2,
176 if (!isDrawingEnabled())
179 setQPainterPen(computeColor(col), ls, lw);
180 bool const do_antialiasing = renderHints() & TextAntialiasing
181 && x1 != x2 && y1 != y2;
182 setRenderHint(Antialiasing, do_antialiasing);
183 drawLine(x1, y1, x2, y2);
184 setRenderHint(Antialiasing, false);
188 void GuiPainter::lines(int const * xp, int const * yp, int np,
193 if (!isDrawingEnabled())
196 // double the size if needed
197 static QVector<QPoint> points(32);
198 if (np > points.size())
199 points.resize(2 * np);
201 bool antialias = false;
202 for (int i = 0; i < np; ++i) {
203 points[i].setX(xp[i]);
204 points[i].setY(yp[i]);
206 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
208 setQPainterPen(computeColor(col), ls, lw);
209 bool const text_is_antialiased = renderHints() & TextAntialiasing;
210 setRenderHint(Antialiasing, antialias && text_is_antialiased);
211 drawPolyline(points.data(), np);
212 setRenderHint(Antialiasing, false);
216 void GuiPainter::rectangle(int x, int y, int w, int h,
221 if (!isDrawingEnabled())
224 setQPainterPen(computeColor(col), ls, lw);
225 drawRect(x, y, w, h);
229 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
231 fillRect(x, y, w, h, guiApp->colorCache().get(col));
235 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
236 int a1, int a2, ColorCode col)
238 if (!isDrawingEnabled())
241 // LyX usings 1/64ths degree, Qt usings 1/16th
242 setQPainterPen(computeColor(col));
243 bool const do_antialiasing = renderHints() & TextAntialiasing;
244 setRenderHint(Antialiasing, do_antialiasing);
245 drawArc(x, y, w, h, a1 / 4, a2 / 4);
246 setRenderHint(Antialiasing, false);
250 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
252 graphics::GuiImage const & qlimage =
253 static_cast<graphics::GuiImage const &>(i);
255 fillRectangle(x, y, w, h, Color_graphicsbg);
257 if (!isDrawingEnabled())
260 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
264 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
267 return text(x, y, s, f);
271 int GuiPainter::smallCapsText(int x, int y,
272 QString const & s, FontInfo const & f)
274 FontInfo smallfont(f);
275 smallfont.decSize().decSize().setShape(UP_SHAPE);
277 QFont const & qfont = guiApp->guiFontLoader().get(f);
278 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
280 setQPainterPen(computeColor(f.realColor()));
282 size_t const ls = s.length();
283 for (unsigned int i = 0; i < ls; ++i) {
284 QChar const c = s[i].toUpper();
290 if (isDrawingEnabled())
291 drawText(x + textwidth, y, c);
292 textwidth += fontMetrics().width(c);
298 int GuiPainter::text(int x, int y, docstring const & s,
304 /* Caution: The following ucs4 to QString conversions work for symbol fonts
305 only because they are no real conversions but simple casts in reality.
306 When we want to draw a symbol or calculate the metrics we pass the position
307 of the symbol in the font (as given in lib/symbols) as a char_type to the
308 frontend. This is just wrong, because the symbol is no UCS4 character at
309 all. You can think of this number as the code point of the symbol in a
310 custom symbol encoding. It works because this char_type is lateron again
311 interpreted as a position in the font again.
312 The correct solution would be to have extra functions for symbols, but that
313 would require to duplicate a lot of frontend and mathed support code.
315 QString str = toqstr(s);
318 // HACK: QT3 refuses to show single compose characters
319 // Still needed with Qt4?
320 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
324 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
328 if (f.realShape() == SMALLCAPS_SHAPE) {
329 textwidth = smallCapsText(x, y, str, f);
330 if (f.underbar() == FONT_ON)
331 underline(f, x, y, textwidth);
335 // Here we use the font width cache instead of
336 // textwidth = fontMetrics().width(str);
337 // because the above is awfully expensive on MacOSX
338 textwidth = fi.metrics.width(s);
339 if (f.underbar() == FONT_ON)
340 underline(f, x, y, textwidth);
342 if (!isDrawingEnabled())
345 // Qt4 does not display a glyph whose codepoint is the
346 // same as that of a soft-hyphen (0x00ad), unless it
347 // occurs at a line-break. As a kludge, we force Qt to
348 // render this glyph using a one-column line.
349 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
350 setQPainterPen(computeColor(f.realColor()));
351 QTextLayout adsymbol(str);
352 adsymbol.setFont(fi.font);
353 adsymbol.beginLayout();
354 QTextLine line = adsymbol.createLine();
355 line.setNumColumns(1);
356 line.setPosition(QPointF(0, -line.ascent()));
357 adsymbol.endLayout();
358 line.draw(this, QPointF(x, y));
362 if (!use_pixmap_cache_) {
363 // don't use the pixmap cache,
364 // draw directly onto the painting device
365 setQPainterPen(computeColor(f.realColor()));
366 if (font() != fi.font)
368 // We need to draw the text as LTR as we use our own bidi code.
369 setLayoutDirection(Qt::LeftToRight);
370 // We need to draw the text as LTR as we use our own bidi code.
371 setLayoutDirection(Qt::LeftToRight);
373 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
374 // << " at " << x << "," << y);
379 QString key = generateStringSignature(str, f);
380 // Warning: Left bearing is in general negative! Only the case
381 // where left bearing is negative is of interest WRT the the
382 // pixmap width and the text x-position.
383 // Only the left bearing of the first character is important
384 // as we always write from left to right, even for
385 // right-to-left languages.
386 int const lb = min(fi.metrics.lbearing(s[0]), 0);
387 int const mA = fi.metrics.maxAscent();
388 if (!QPixmapCache::find(key, pm)) {
389 // Only the right bearing of the last character is
390 // important as we always write from left to right,
391 // even for right-to-left languages.
392 int const rb = fi.metrics.rbearing(s[s.size()-1]);
393 int const w = textwidth + rb - lb;
394 int const mD = fi.metrics.maxDescent();
395 int const h = mA + mD;
397 pm.fill(Qt::transparent);
399 p.setQPainterPen(computeColor(f.realColor()));
400 if (p.font() != fi.font)
402 // We need to draw the text as LTR as we use our own bidi code.
403 p.setLayoutDirection(Qt::LeftToRight);
404 p.drawText(-lb, mA, str);
405 QPixmapCache::insert(key, pm);
406 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
407 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
410 // Draw the cached pixmap.
411 drawPixmap(x + lb, y - mA, pm);
417 static int max(int a, int b) { return a > b ? a : b; }
420 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
423 fillRectangle(x, y, w, h, Color_buttonhoverbg);
425 fillRectangle(x, y, w, h, Color_buttonbg);
426 buttonFrame(x, y, w, h);
430 void GuiPainter::buttonFrame(int x, int y, int w, int h)
432 line(x, y, x, y + h - 1, Color_buttonframe);
433 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
434 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
435 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
439 void GuiPainter::rectText(int x, int y, docstring const & str,
440 FontInfo const & font, ColorCode back, ColorCode frame)
446 FontMetrics const & fm = theFontMetrics(font);
447 fm.rectText(str, width, ascent, descent);
449 if (back != Color_none)
450 fillRectangle(x + 1, y - ascent + 1, width - 1,
451 ascent + descent - 1, back);
453 if (frame != Color_none)
454 rectangle(x, y - ascent, width, ascent + descent, frame);
456 text(x + 3, y, str, font);
460 void GuiPainter::buttonText(int x, int y, docstring const & str,
461 FontInfo const & font, bool mouseHover)
467 FontMetrics const & fm = theFontMetrics(font);
468 fm.buttonText(str, width, ascent, descent);
470 button(x + 1, y - ascent, width - 2, descent + ascent, mouseHover);
471 text(x + 4, y - 1, str, font);
475 int GuiPainter::preeditText(int x, int y, char_type c,
476 FontInfo const & font, preedit_style style)
478 FontInfo temp_font = font;
479 FontMetrics const & fm = theFontMetrics(font);
480 int ascent = fm.maxAscent();
481 int descent = fm.maxDescent();
482 int height = ascent + descent;
483 int width = fm.width(c);
486 case preedit_default:
487 // default unselecting mode.
488 fillRectangle(x, y - height + 1, width, height, Color_background);
489 dashedUnderline(font, x, y - descent + 1, width);
491 case preedit_selecting:
492 // We are in selecting mode: white text on black background.
493 fillRectangle(x, y - height + 1, width, height, Color_black);
494 temp_font.setColor(Color_white);
497 // The character comes with a cursor.
498 fillRectangle(x, y - height + 1, width, height, Color_background);
499 underline(font, x, y - descent + 1, width);
502 text(x, y - descent + 1, c, temp_font);
508 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
510 FontMetrics const & fm = theFontMetrics(f);
512 int const below = max(fm.maxDescent() / 2, 2);
513 int const height = max((fm.maxDescent() / 4) - 1, 1);
516 line(x, y + below, x + width, y + below, f.color());
518 fillRectangle(x, y + below, width, below + height, f.color());
522 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
524 FontMetrics const & fm = theFontMetrics(f);
526 int const below = max(fm.maxDescent() / 2, 2);
527 int height = max((fm.maxDescent() / 4) - 1, 1);
532 for (int n = 0; n != height; ++n)
533 line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
536 } // namespace frontend