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(Color 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(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())
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
, Color 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
, Color 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
);
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
);
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())
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
));
382 if (use_pixmap_cache_
) {
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
);
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) {
409 pm
.fill(Qt::transparent
);
411 p
.setQPainterPen(computeColor(f
.realColor()));
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
422 // Draw the new cached pixmap.
423 drawPixmap(x
+ lb
, y
- mA
, pm
);
429 // don't use the pixmap cache,
430 // draw directly onto the painting device
431 setQPainterPen(computeColor(f
.realColor()));
434 // We need to draw the text as LTR as we use our own bidi code.
435 QPainter::setLayoutDirection(Qt::LeftToRight
);
437 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
438 // << " at " << x << "," << y);
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
)
449 fillRectangle(x
, y
, w
, h
, Color_buttonhoverbg
);
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
)
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
)
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
);
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
);
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
);
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
);
530 text(x
, y
- descent
+ 1, c
, temp_font
);
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);
555 line(x
, y
+ below
, x
+ width
, y
+ below
, f
.realColor());
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;
569 line(x
, y
- middle
, x
+ width
, y
- middle
, f
.realColor());
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);
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
));
594 int const xend
= x
+ width
;
596 //FIXME: I am not sure if Antialiasing gives the best effect.
597 //setRenderHint(Antialiasing, true);
600 drawLine(x
, y
- height
, x
+ step
, y
+ height
);
602 drawLine(x
, y
+ height
, x
+ 2, y
+ height
);
605 //setRenderHint(Antialiasing, false);
608 } // namespace frontend