GuiPrintNomencl.{cpp,h}:
[lyx.git] / src / frontends / qt4 / GuiPainter.cpp
blobafa2182eb671f4ee3e53208c0df13efc4a273e86
1 /**
2 * \file GuiPainter.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author John Levon
7 * \author Abdelrazak Younes
9 * Full author contact details are available in file CREDITS.
12 #include <config.h>
14 #include "GuiPainter.h"
16 #include "ColorCache.h"
17 #include "GuiApplication.h"
18 #include "GuiFontLoader.h"
19 #include "GuiFontMetrics.h"
20 #include "GuiImage.h"
21 #include "qt_helpers.h"
23 #include "FontInfo.h"
24 #include "Language.h"
25 #include "LyXRC.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.
37 #if defined(Q_WS_X11)
38 #define USE_PIXMAP_CACHE 0
39 #else
40 #define USE_PIXMAP_CACHE 1
41 #endif
43 using namespace std;
45 namespace lyx {
46 namespace frontend {
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()
61 QPainter::end();
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_)
70 return;
72 current_color_ = col;
73 current_ls_ = ls;
74 current_lw_ = lw;
76 QPen pen = QPainter::pen();
77 pen.setColor(col);
79 switch (ls) {
80 case line_solid: pen.setStyle(Qt::SolidLine); break;
81 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
84 switch (lw) {
85 case line_thin: pen.setWidth(0); break;
86 case line_thick: pen.setWidth(3); break;
89 setPen(pen);
93 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
95 QString sig = str;
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())));
111 return sig;
115 QColor GuiPainter::computeColor(Color col)
117 return filterColor(guiApp->colorCache().get(col));
121 QColor GuiPainter::filterColor(QColor const & col)
123 if (monochrome_min_.empty())
124 return col;
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);
138 QColor c;
139 c.setRgbF(
140 v * (minr - maxr) + maxr,
141 v * (ming - maxg) + maxg,
142 v * (minb - maxb) + maxb);
143 return c;
147 void GuiPainter::enterMonochromeMode(Color const & min, Color 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, Color col)
166 if (!isDrawingEnabled())
167 return;
169 setQPainterPen(computeColor(col));
170 drawPoint(x, y);
174 void GuiPainter::line(int x1, int y1, int x2, int y2,
175 Color col,
176 line_style ls,
177 line_width lw)
179 if (!isDrawingEnabled())
180 return;
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,
192 Color col,
193 line_style ls,
194 line_width lw)
196 if (!isDrawingEnabled())
197 return;
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]);
208 if (i != 0)
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,
220 Color col,
221 line_style ls,
222 line_width lw)
224 if (!isDrawingEnabled())
225 return;
227 setQPainterPen(computeColor(col), ls, lw);
228 drawRect(x, y, w, h);
232 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
234 if (!isDrawingEnabled())
235 return;
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, Color col)
244 if (!isDrawingEnabled())
245 return;
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())
264 return;
266 drawImage(x, y, qlimage.image(), 0, 0, w, h);
270 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
272 docstring s(1, c);
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()));
287 int textwidth = 0;
288 size_t const ls = s.length();
289 for (unsigned int i = 0; i < ls; ++i) {
290 QChar const c = s[i].toUpper();
291 if (c != s.at(i)) {
292 setFont(qsmallfont);
293 } else {
294 setFont(qfont);
296 if (isDrawingEnabled())
297 drawText(x + textwidth, y, c);
298 textwidth += fontMetrics().width(c);
300 return textwidth;
304 int GuiPainter::text(int x, int y, docstring const & s,
305 FontInfo const & f)
307 if (s.empty())
308 return 0;
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);
323 #if 0
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)
327 str = ' ' + str;
328 #endif
330 QFont const & ff = getFont(f);
331 GuiFontMetrics const & fm = getFontMetrics(f);
333 int textwidth;
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);
339 if (f.strikeout() == FONT_ON)
340 strikeoutLine(f, x, y, textwidth);
341 if (f.uuline() == FONT_ON)
342 doubleUnderline(f, x, y, textwidth);
343 if (f.uwave() == FONT_ON)
344 wavyHorizontalLine(x, y, textwidth, f.realColor().baseColor);
345 return textwidth;
348 // Here we use the font width cache instead of
349 // textwidth = fontMetrics().width(str);
350 // because the above is awfully expensive on MacOSX
351 textwidth = fm.width(s);
352 if (f.underbar() == FONT_ON)
353 underline(f, x, y, textwidth);
354 if (f.strikeout() == FONT_ON)
355 strikeoutLine(f, x, y, textwidth);
356 if (f.uuline() == FONT_ON)
357 doubleUnderline(f, x, y, textwidth);
358 if (f.uwave() == FONT_ON)
359 // f.color() doesn't work on some circumstances
360 wavyHorizontalLine(x, y, textwidth, f.realColor().baseColor);
362 if (!isDrawingEnabled())
363 return textwidth;
365 // Qt4 does not display a glyph whose codepoint is the
366 // same as that of a soft-hyphen (0x00ad), unless it
367 // occurs at a line-break. As a kludge, we force Qt to
368 // render this glyph using a one-column line.
369 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
370 setQPainterPen(computeColor(f.realColor()));
371 QTextLayout adsymbol(str);
372 adsymbol.setFont(ff);
373 adsymbol.beginLayout();
374 QTextLine line = adsymbol.createLine();
375 line.setNumColumns(1);
376 line.setPosition(QPointF(0, -line.ascent()));
377 adsymbol.endLayout();
378 line.draw(this, QPointF(x, y));
379 return textwidth;
382 if (use_pixmap_cache_) {
383 QPixmap pm;
384 QString key = generateStringSignature(str, f);
386 // Warning: Left bearing is in general negative! Only the case
387 // where left bearing is negative is of interest WRT the
388 // pixmap width and the text x-position.
389 // Only the left bearing of the first character is important
390 // as we always write from left to right, even for
391 // right-to-left languages.
392 int const lb = min(fm.lbearing(s[0]), 0);
393 int const mA = fm.maxAscent();
394 if (QPixmapCache::find(key, pm)) {
395 // Draw the cached pixmap.
396 drawPixmap(x + lb, y - mA, pm);
397 return textwidth;
400 // Only the right bearing of the last character is
401 // important as we always write from left to right,
402 // even for right-to-left languages.
403 int const rb = fm.rbearing(s[s.size()-1]);
404 int const w = textwidth + rb - lb;
405 int const mD = fm.maxDescent();
406 int const h = mA + mD;
407 if (w > 0 && h > 0) {
408 pm = QPixmap(w, h);
409 pm.fill(Qt::transparent);
410 GuiPainter p(&pm);
411 p.setQPainterPen(computeColor(f.realColor()));
412 if (p.font() != ff)
413 p.setFont(ff);
414 // We need to draw the text as LTR as we use our own bidi code.
415 p.setLayoutDirection(Qt::LeftToRight);
416 p.drawText(-lb, mA, str);
417 QPixmapCache::insert(key, pm);
418 //LYXERR(Debug::PAINTING, "h=" << h << " mA=" << mA << " mD=" << mD
419 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
420 // << " rb=" << rb);
422 // Draw the new cached pixmap.
423 drawPixmap(x + lb, y - mA, pm);
425 return textwidth;
429 // don't use the pixmap cache,
430 // draw directly onto the painting device
431 setQPainterPen(computeColor(f.realColor()));
432 if (font() != ff)
433 setFont(ff);
434 // We need to draw the text as LTR as we use our own bidi code.
435 QPainter::setLayoutDirection(Qt::LeftToRight);
436 drawText(x, y, str);
437 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
438 // << " at " << x << "," << y);
439 return textwidth;
443 static int max(int a, int b) { return a > b ? a : b; }
446 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
448 if (mouseHover)
449 fillRectangle(x, y, w, h, Color_buttonhoverbg);
450 else
451 fillRectangle(x, y, w, h, Color_buttonbg);
452 buttonFrame(x, y, w, h);
456 void GuiPainter::buttonFrame(int x, int y, int w, int h)
458 line(x, y, x, y + h - 1, Color_buttonframe);
459 line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
460 line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
461 line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
465 void GuiPainter::rectText(int x, int y, docstring const & str,
466 FontInfo const & font, Color back, Color frame)
468 int width;
469 int ascent;
470 int descent;
472 FontMetrics const & fm = theFontMetrics(font);
473 fm.rectText(str, width, ascent, descent);
475 if (back != Color_none)
476 fillRectangle(x + 1, y - ascent + 1, width - 1,
477 ascent + descent - 1, back);
479 if (frame != Color_none)
480 rectangle(x, y - ascent, width, ascent + descent, frame);
482 text(x + 3, y, str, font);
486 void GuiPainter::buttonText(int x, int y, docstring const & str,
487 FontInfo const & font, bool mouseHover)
489 int width;
490 int ascent;
491 int descent;
493 FontMetrics const & fm = theFontMetrics(font);
494 fm.buttonText(str, width, ascent, descent);
496 static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
498 button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
499 text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
503 int GuiPainter::preeditText(int x, int y, char_type c,
504 FontInfo const & font, preedit_style style)
506 FontInfo temp_font = font;
507 FontMetrics const & fm = theFontMetrics(font);
508 int ascent = fm.maxAscent();
509 int descent = fm.maxDescent();
510 int height = ascent + descent;
511 int width = fm.width(c);
513 switch (style) {
514 case preedit_default:
515 // default unselecting mode.
516 fillRectangle(x, y - height + 1, width, height, Color_background);
517 dashedUnderline(font, x, y - descent + 1, width);
518 break;
519 case preedit_selecting:
520 // We are in selecting mode: white text on black background.
521 fillRectangle(x, y - height + 1, width, height, Color_black);
522 temp_font.setColor(Color_white);
523 break;
524 case preedit_cursor:
525 // The character comes with a cursor.
526 fillRectangle(x, y - height + 1, width, height, Color_background);
527 underline(font, x, y - descent + 1, width);
528 break;
530 text(x, y - descent + 1, c, temp_font);
532 return width;
536 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
538 FontMetrics const & fm = theFontMetrics(f);
540 int const below = max(fm.maxDescent() / 2, 2);
542 line(x, y + below, x + width, y + below, f.realColor());
543 line(x, y + below - 2, x + width, y + below - 2, f.realColor());
547 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
549 FontMetrics const & fm = theFontMetrics(f);
551 int const below = max(fm.maxDescent() / 2, 2);
552 int const height = max((fm.maxDescent() / 4) - 1, 1);
554 if (height < 2)
555 line(x, y + below, x + width, y + below, f.realColor());
556 else
557 fillRectangle(x, y + below, width, below + height, f.realColor());
561 void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
563 FontMetrics const & fm = theFontMetrics(f);
565 int const middle = max((fm.maxHeight() / 4), 1);
566 int const height = middle/3;
568 if (height < 2)
569 line(x, y - middle, x + width, y - middle, f.realColor());
570 else
571 fillRectangle(x, y - middle, width, height, f.realColor());
575 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
577 FontMetrics const & fm = theFontMetrics(f);
579 int const below = max(fm.maxDescent() / 2, 2);
580 int height = max((fm.maxDescent() / 4) - 1, 1);
582 if (height >= 2)
583 height += below;
585 for (int n = 0; n != height; ++n)
586 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
590 void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
592 setQPainterPen(computeColor(col));
593 int const step = 4;
594 int const xend = x + width;
595 int height = 1;
596 //FIXME: I am not sure if Antialiasing gives the best effect.
597 //setRenderHint(Antialiasing, true);
598 while (x < xend) {
599 height = - height;
600 drawLine(x, y - height, x + step, y + height);
601 x += step;
602 drawLine(x, y + height, x + 2, y + height);
603 x += 2;
605 //setRenderHint(Antialiasing, false);
608 } // namespace frontend
609 } // namespace lyx