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"
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.
34 #if (QT_VERSION < 0x040200) || defined(Q_WS_X11)
35 #define USE_PIXMAP_CACHE 0
37 #define USE_PIXMAP_CACHE 1
48 bool const usePixmapCache = USE_PIXMAP_CACHE;
50 QString generateStringSignature(QString const & str, Font const & f)
53 sig.append(QChar(static_cast<short>(f.family())));
54 sig.append(QChar(static_cast<short>(f.series())));
55 sig.append(QChar(static_cast<short>(f.realShape())));
56 sig.append(QChar(static_cast<short>(f.size())));
57 sig.append(QChar(static_cast<short>(f.color())));
63 GuiPainter::GuiPainter(QPaintDevice * device)
64 : QPainter(device), Painter()
66 // new QPainter has default QPen:
67 current_color_ = Color::black;
68 current_ls_ = line_solid;
69 current_lw_ = line_thin;
73 GuiPainter::~GuiPainter()
76 //lyxerr << "GuiPainter::end()" << endl;
80 void GuiPainter::setQPainterPen(Color_color col,
81 Painter::line_style ls, Painter::line_width lw)
83 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
90 QPen pen = QPainter::pen();
92 pen.setColor(guiApp->colorCache().get(col));
95 case line_solid: pen.setStyle(Qt::SolidLine); break;
96 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
100 case line_thin: pen.setWidth(0); break;
101 case line_thick: pen.setWidth(3); break;
108 void GuiPainter::point(int x, int y, Color_color col)
110 if (!isDrawingEnabled())
118 void GuiPainter::line(int x1, int y1, int x2, int y2,
123 if (!isDrawingEnabled())
126 setQPainterPen(col, ls, lw);
127 bool const do_antialiasing = renderHints() & TextAntialiasing
128 && x1 != x2 && y1 != y2;
129 setRenderHint(Antialiasing, do_antialiasing);
130 drawLine(x1, y1, x2, y2);
131 setRenderHint(Antialiasing, false);
135 void GuiPainter::lines(int const * xp, int const * yp, int np,
140 if (!isDrawingEnabled())
143 // double the size if needed
144 static QVector<QPoint> points(32);
145 if (np > points.size())
146 points.resize(2 * np);
148 bool antialias = false;
149 for (int i = 0; i < np; ++i) {
150 points[i].setX(xp[i]);
151 points[i].setY(yp[i]);
153 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
155 setQPainterPen(col, ls, lw);
156 bool const text_is_antialiased = renderHints() & TextAntialiasing;
157 setRenderHint(Antialiasing, antialias && text_is_antialiased);
158 drawPolyline(points.data(), np);
159 setRenderHint(Antialiasing, false);
163 void GuiPainter::rectangle(int x, int y, int w, int h,
168 if (!isDrawingEnabled())
171 setQPainterPen(col, ls, lw);
172 drawRect(x, y, w, h);
176 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
178 fillRect(x, y, w, h, guiApp->colorCache().get(col));
182 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
183 int a1, int a2, Color_color col)
185 if (!isDrawingEnabled())
188 // LyX usings 1/64ths degree, Qt usings 1/16th
190 bool const do_antialiasing = renderHints() & TextAntialiasing;
191 setRenderHint(Antialiasing, do_antialiasing);
192 drawArc(x, y, w, h, a1 / 4, a2 / 4);
193 setRenderHint(Antialiasing, false);
197 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
199 graphics::GuiImage const & qlimage =
200 static_cast<graphics::GuiImage const &>(i);
202 fillRectangle(x, y, w, h, Color::graphicsbg);
204 if (!isDrawingEnabled())
207 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
211 int GuiPainter::text(int x, int y, char_type c, Font const & f)
214 return text(x, y, s, f);
218 int GuiPainter::smallCapsText(int x, int y,
219 QString const & s, Font const & f)
222 smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
224 QFont const & qfont = guiApp->guiFontLoader().get(f);
225 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
227 setQPainterPen(f.realColor());
229 size_t const ls = s.length();
230 for (unsigned int i = 0; i < ls; ++i) {
231 QChar const c = s[i].toUpper();
237 if (isDrawingEnabled())
238 drawText(x + textwidth, y, c);
239 textwidth += fontMetrics().width(c);
245 int GuiPainter::text(int x, int y, docstring const & s,
248 /* Caution: The following ucs4 to QString conversions work for symbol fonts
249 only because they are no real conversions but simple casts in reality.
250 When we want to draw a symbol or calculate the metrics we pass the position
251 of the symbol in the font (as given in lib/symbols) as a char_type to the
252 frontend. This is just wrong, because the symbol is no UCS4 character at
253 all. You can think of this number as the code point of the symbol in a
254 custom symbol encoding. It works because this char_type is lateron again
255 interpreted as a position in the font again.
256 The correct solution would be to have extra functions for symbols, but that
257 would require to duplicate a lot of frontend and mathed support code.
259 QString str = toqstr(s);
262 // HACK: QT3 refuses to show single compose characters
263 // Still needed with Qt4?
264 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
268 QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
272 if (f.realShape() == Font::SMALLCAPS_SHAPE) {
273 textwidth = smallCapsText(x, y, str, f);
274 if (f.underbar() == Font::ON)
275 underline(f, x, y, textwidth);
279 // Here we use the font width cache instead of
280 // textwidth = fontMetrics().width(str);
281 // because the above is awfully expensive on MacOSX
282 textwidth = fi.metrics->width(s);
283 if (f.underbar() == Font::ON)
284 underline(f, x, y, textwidth);
286 if (!isDrawingEnabled())
289 // Qt4 does not display a glyph whose codepoint is the
290 // same as that of a soft-hyphen (0x00ad), unless it
291 // occurs at a line-break. As a kludge, we force Qt to
292 // render this glyph using a one-column line.
293 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
294 setQPainterPen(f.realColor());
295 QTextLayout adsymbol(str);
296 adsymbol.setFont(fi.font);
297 adsymbol.beginLayout();
298 QTextLine line = adsymbol.createLine();
299 line.setNumColumns(1);
300 line.setPosition(QPointF(0, -line.ascent()));
301 adsymbol.endLayout();
302 line.draw(this, QPointF(x, y));
306 if (!usePixmapCache) {
307 // don't use the pixmap cache,
308 // draw directly onto the painting device
309 setQPainterPen(f.realColor());
310 if (font() != fi.font)
312 // We need to draw the text as LTR as we use our own bidi code.
313 setLayoutDirection(Qt::LeftToRight);
314 // We need to draw the text as LTR as we use our own bidi code.
315 setLayoutDirection(Qt::LeftToRight);
317 LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
318 << " at " << x << "," << y << std::endl;
323 QString key = generateStringSignature(str, f);
324 // Warning: Left bearing is in general negative! Only the case
325 // where left bearing is negative is of interest WRT the the
326 // pixmap width and the text x-position.
327 // Only the left bearing of the first character is important
328 // as we always write from left to right, even for
329 // right-to-left languages.
330 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
331 int const mA = fi.metrics->maxAscent();
332 if (!QPixmapCache::find(key, pm)) {
333 // Only the right bearing of the last character is
334 // important as we always write from left to right,
335 // even for right-to-left languages.
336 int const rb = fi.metrics->rbearing(s[s.size()-1]);
337 int const w = textwidth + rb - lb;
338 int const mD = fi.metrics->maxDescent();
339 int const h = mA + mD;
341 pm.fill(Qt::transparent);
343 p.setQPainterPen(f.realColor());
344 if (p.font() != fi.font)
346 // We need to draw the text as LTR as we use our own bidi code.
347 p.setLayoutDirection(Qt::LeftToRight);
348 p.drawText(-lb, mA, str);
349 QPixmapCache::insert(key, pm);
350 LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
351 << " w=" << w << " lb=" << lb << " tw=" << textwidth
352 << " rb=" << rb << endl;
354 // Draw the cached pixmap.
355 drawPixmap(x + lb, y - mA, pm);
361 } // namespace frontend