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"
26 #include "support/unicode.h"
28 #include <QPixmapCache>
29 #include <QTextLayout>
31 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
32 // drawing text. This is especially useful for older PPC/Mac systems.
33 #if (QT_VERSION < 0x040200) || defined(Q_WS_X11)
34 #define USE_PIXMAP_CACHE 0
36 #define USE_PIXMAP_CACHE 1
47 bool const usePixmapCache = USE_PIXMAP_CACHE;
49 QString generateStringSignature(QString const & str, Font const & f)
52 sig.append(QChar(static_cast<short>(f.family())));
53 sig.append(QChar(static_cast<short>(f.series())));
54 sig.append(QChar(static_cast<short>(f.realShape())));
55 sig.append(QChar(static_cast<short>(f.size())));
56 sig.append(QChar(static_cast<short>(f.color())));
62 GuiPainter::GuiPainter(QPaintDevice * device)
63 : QPainter(device), Painter()
65 // new QPainter has default QPen:
66 current_color_ = Color_black;
67 current_ls_ = line_solid;
68 current_lw_ = line_thin;
72 GuiPainter::~GuiPainter()
75 //lyxerr << "GuiPainter::end()" << endl;
79 void GuiPainter::setQPainterPen(ColorCode col,
80 Painter::line_style ls, Painter::line_width lw)
82 if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
89 QPen pen = QPainter::pen();
91 pen.setColor(guiApp->colorCache().get(col));
94 case line_solid: pen.setStyle(Qt::SolidLine); break;
95 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
99 case line_thin: pen.setWidth(0); break;
100 case line_thick: pen.setWidth(3); break;
107 void GuiPainter::point(int x, int y, ColorCode col)
109 if (!isDrawingEnabled())
117 void GuiPainter::line(int x1, int y1, int x2, int y2,
122 if (!isDrawingEnabled())
125 setQPainterPen(col, ls, lw);
126 bool const do_antialiasing = renderHints() & TextAntialiasing
127 && x1 != x2 && y1 != y2;
128 setRenderHint(Antialiasing, do_antialiasing);
129 drawLine(x1, y1, x2, y2);
130 setRenderHint(Antialiasing, false);
134 void GuiPainter::lines(int const * xp, int const * yp, int np,
139 if (!isDrawingEnabled())
142 // double the size if needed
143 static QVector<QPoint> points(32);
144 if (np > points.size())
145 points.resize(2 * np);
147 bool antialias = false;
148 for (int i = 0; i < np; ++i) {
149 points[i].setX(xp[i]);
150 points[i].setY(yp[i]);
152 antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
154 setQPainterPen(col, ls, lw);
155 bool const text_is_antialiased = renderHints() & TextAntialiasing;
156 setRenderHint(Antialiasing, antialias && text_is_antialiased);
157 drawPolyline(points.data(), np);
158 setRenderHint(Antialiasing, false);
162 void GuiPainter::rectangle(int x, int y, int w, int h,
167 if (!isDrawingEnabled())
170 setQPainterPen(col, ls, lw);
171 drawRect(x, y, w, h);
175 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
177 fillRect(x, y, w, h, guiApp->colorCache().get(col));
181 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
182 int a1, int a2, ColorCode col)
184 if (!isDrawingEnabled())
187 // LyX usings 1/64ths degree, Qt usings 1/16th
189 bool const do_antialiasing = renderHints() & TextAntialiasing;
190 setRenderHint(Antialiasing, do_antialiasing);
191 drawArc(x, y, w, h, a1 / 4, a2 / 4);
192 setRenderHint(Antialiasing, false);
196 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
198 graphics::GuiImage const & qlimage =
199 static_cast<graphics::GuiImage const &>(i);
201 fillRectangle(x, y, w, h, Color_graphicsbg);
203 if (!isDrawingEnabled())
206 drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
210 int GuiPainter::text(int x, int y, char_type c, Font const & f)
213 return text(x, y, s, f);
217 int GuiPainter::smallCapsText(int x, int y,
218 QString const & s, Font const & f)
221 smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
223 QFont const & qfont = guiApp->guiFontLoader().get(f);
224 QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
226 setQPainterPen(f.realColor());
228 size_t const ls = s.length();
229 for (unsigned int i = 0; i < ls; ++i) {
230 QChar const c = s[i].toUpper();
236 if (isDrawingEnabled())
237 drawText(x + textwidth, y, c);
238 textwidth += fontMetrics().width(c);
244 int GuiPainter::text(int x, int y, docstring const & s,
250 /* Caution: The following ucs4 to QString conversions work for symbol fonts
251 only because they are no real conversions but simple casts in reality.
252 When we want to draw a symbol or calculate the metrics we pass the position
253 of the symbol in the font (as given in lib/symbols) as a char_type to the
254 frontend. This is just wrong, because the symbol is no UCS4 character at
255 all. You can think of this number as the code point of the symbol in a
256 custom symbol encoding. It works because this char_type is lateron again
257 interpreted as a position in the font again.
258 The correct solution would be to have extra functions for symbols, but that
259 would require to duplicate a lot of frontend and mathed support code.
261 QString str = toqstr(s);
264 // HACK: QT3 refuses to show single compose characters
265 // Still needed with Qt4?
266 if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
270 GuiFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
274 if (f.realShape() == Font::SMALLCAPS_SHAPE) {
275 textwidth = smallCapsText(x, y, str, f);
276 if (f.underbar() == Font::ON)
277 underline(f, x, y, textwidth);
281 // Here we use the font width cache instead of
282 // textwidth = fontMetrics().width(str);
283 // because the above is awfully expensive on MacOSX
284 textwidth = fi.metrics->width(s);
285 if (f.underbar() == Font::ON)
286 underline(f, x, y, textwidth);
288 if (!isDrawingEnabled())
291 // Qt4 does not display a glyph whose codepoint is the
292 // same as that of a soft-hyphen (0x00ad), unless it
293 // occurs at a line-break. As a kludge, we force Qt to
294 // render this glyph using a one-column line.
295 if (s.size() == 1 && str[0].unicode() == 0x00ad) {
296 setQPainterPen(f.realColor());
297 QTextLayout adsymbol(str);
298 adsymbol.setFont(fi.font);
299 adsymbol.beginLayout();
300 QTextLine line = adsymbol.createLine();
301 line.setNumColumns(1);
302 line.setPosition(QPointF(0, -line.ascent()));
303 adsymbol.endLayout();
304 line.draw(this, QPointF(x, y));
308 if (!usePixmapCache) {
309 // don't use the pixmap cache,
310 // draw directly onto the painting device
311 setQPainterPen(f.realColor());
312 if (font() != fi.font)
314 // We need to draw the text as LTR as we use our own bidi code.
315 setLayoutDirection(Qt::LeftToRight);
316 // We need to draw the text as LTR as we use our own bidi code.
317 setLayoutDirection(Qt::LeftToRight);
319 //LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
320 // << " at " << x << "," << y << std::endl;
325 QString key = generateStringSignature(str, f);
326 // Warning: Left bearing is in general negative! Only the case
327 // where left bearing is negative is of interest WRT the the
328 // pixmap width and the text x-position.
329 // Only the left bearing of the first character is important
330 // as we always write from left to right, even for
331 // right-to-left languages.
332 int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
333 int const mA = fi.metrics->maxAscent();
334 if (!QPixmapCache::find(key, pm)) {
335 // Only the right bearing of the last character is
336 // important as we always write from left to right,
337 // even for right-to-left languages.
338 int const rb = fi.metrics->rbearing(s[s.size()-1]);
339 int const w = textwidth + rb - lb;
340 int const mD = fi.metrics->maxDescent();
341 int const h = mA + mD;
343 pm.fill(Qt::transparent);
345 p.setQPainterPen(f.realColor());
346 if (p.font() != fi.font)
348 // We need to draw the text as LTR as we use our own bidi code.
349 p.setLayoutDirection(Qt::LeftToRight);
350 p.drawText(-lb, mA, str);
351 QPixmapCache::insert(key, pm);
352 //LYXERR(Debug::PAINTING) << "h=" << h << " mA=" << mA << " mD=" << mD
353 // << " w=" << w << " lb=" << lb << " tw=" << textwidth
354 // << " rb=" << rb << endl;
356 // Draw the cached pixmap.
357 drawPixmap(x + lb, y - mA, pm);
363 } // namespace frontend