]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/QLPainter.cpp
Transfer Text::drawSelection() from InsetText::drawSelection() to InsetText::draw...
[lyx.git] / src / frontends / qt4 / QLPainter.cpp
1 /**
2  * \file QLPainter.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author John Levon
7  * \author Abdelrazak Younes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "QLPainter.h"
15
16 #include "GuiApplication.h"
17 #include "GuiFontMetrics.h"
18 #include "QLImage.h"
19
20 #include "GuiApplication.h"
21 #include "qt_helpers.h"
22
23 #include "debug.h"
24 #include "Language.h"
25 #include "Color.h"
26
27 #include "support/unicode.h"
28
29 #include <QPixmapCache>
30 #include <QTextLayout>
31
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
36 #else
37 #define USE_PIXMAP_CACHE 1
38 #endif
39
40 using std::endl;
41 using std::string;
42
43 namespace lyx {
44 namespace frontend {
45
46 namespace {
47
48 bool const usePixmapCache = USE_PIXMAP_CACHE;
49
50 QString generateStringSignature(QString const & str, Font const & f)
51 {
52         QString sig = str;
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())));
58         return sig;
59 }
60
61 } // anon namespace
62
63 QLPainter::QLPainter(QPaintDevice * device)
64         : QPainter(device), Painter()
65 {
66         // new QPainter has default QPen:
67         current_color_ = Color::black;
68         current_ls_ = line_solid;
69         current_lw_ = line_thin;
70 }
71
72
73 QLPainter::~QLPainter()
74 {
75         QPainter::end();
76         //lyxerr << "QLPainter::end()" << endl;
77 }
78
79
80 void QLPainter::setQPainterPen(Color_color col,
81         Painter::line_style ls, Painter::line_width lw)
82 {
83         if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
84                 return;
85
86         current_color_ = col;
87         current_ls_ = ls;
88         current_lw_ = lw;
89
90         QPen pen = QPainter::pen();
91
92         pen.setColor(guiApp->colorCache().get(col));
93
94         switch (ls) {
95                 case line_solid: pen.setStyle(Qt::SolidLine); break;
96                 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
97         }
98
99         switch (lw) {
100                 case line_thin: pen.setWidth(0); break;
101                 case line_thick: pen.setWidth(3); break;
102         }
103
104         setPen(pen);
105 }
106
107
108 void QLPainter::point(int x, int y, Color_color col)
109 {
110         if (!isDrawingEnabled())
111                 return;
112
113         setQPainterPen(col);
114         drawPoint(x, y);
115 }
116
117
118 void QLPainter::line(int x1, int y1, int x2, int y2,
119         Color_color col,
120         line_style ls,
121         line_width lw)
122 {
123         if (!isDrawingEnabled())
124                 return;
125
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);
132 }
133
134
135 void QLPainter::lines(int const * xp, int const * yp, int np,
136         Color_color col,
137         line_style ls,
138         line_width lw)
139 {
140         if (!isDrawingEnabled())
141                 return;
142
143         // double the size if needed
144         static QVector<QPoint> points(32);
145         if (np > points.size())
146                 points.resize(2 * np);
147
148         bool antialias = false;
149         for (int i = 0; i < np; ++i) {
150                 points[i].setX(xp[i]);
151                 points[i].setY(yp[i]);
152                 if (i != 0)
153                         antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
154         }
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);
160 }
161
162
163 void QLPainter::rectangle(int x, int y, int w, int h,
164         Color_color col,
165         line_style ls,
166         line_width lw)
167 {
168         if (!isDrawingEnabled())
169                 return;
170
171         setQPainterPen(col, ls, lw);
172         drawRect(x, y, w, h);
173 }
174
175
176 void QLPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
177 {
178         fillRect(x, y, w, h, guiApp->colorCache().get(col));
179 }
180
181
182 void QLPainter::arc(int x, int y, unsigned int w, unsigned int h,
183         int a1, int a2, Color_color col)
184 {
185         if (!isDrawingEnabled())
186                 return;
187
188         // LyX usings 1/64ths degree, Qt usings 1/16th
189         setQPainterPen(col);
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);
194 }
195
196
197 void QLPainter::image(int x, int y, int w, int h, graphics::Image const & i)
198 {
199         graphics::QLImage const & qlimage =
200                 static_cast<graphics::QLImage const &>(i);
201
202         fillRectangle(x, y, w, h, Color::graphicsbg);
203
204         if (!isDrawingEnabled())
205                 return;
206
207         drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
208 }
209
210
211 int QLPainter::text(int x, int y, char_type c, Font const & f)
212 {
213         docstring s(1, c);
214         return text(x, y, s, f);
215 }
216
217
218 int QLPainter::smallCapsText(int x, int y,
219         QString const & s, Font const & f)
220 {
221         Font smallfont(f);
222         smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
223
224         QFont const & qfont = guiApp->guiFontLoader().get(f);
225         QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
226
227         setQPainterPen(f.realColor());
228         int textwidth = 0;
229         size_t const ls = s.length();
230         for (unsigned int i = 0; i < ls; ++i) {
231                 QChar const c = s[i].toUpper();
232                 if (c != s.at(i)) {
233                         setFont(qsmallfont);
234                 } else {
235                         setFont(qfont);
236                 }
237                 if (isDrawingEnabled())
238                         drawText(x + textwidth, y, c);
239                 textwidth += fontMetrics().width(c);
240         }
241         return textwidth;
242 }
243
244
245 int QLPainter::text(int x, int y, docstring const & s,
246                 Font const & f)
247 {
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.
258         */
259         QString str = toqstr(s);
260
261 #if 0
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)
265                 str = ' ' + str;
266 #endif
267
268         QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
269
270         int textwidth;
271
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);
276                 return textwidth;
277         }
278
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);
285
286         if (!isDrawingEnabled())
287                 return textwidth;
288
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));
303                 return textwidth;
304         }
305
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)
311                         setFont(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);
316                 drawText(x, y, str);
317                 LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
318                         << " at " << x << "," << y << std::endl;
319                 return textwidth;
320         }
321
322         QPixmap pm;
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;
340                 pm = QPixmap(w, h);
341                 pm.fill(Qt::transparent);
342                 QLPainter p(&pm);
343                 p.setQPainterPen(f.realColor());
344                 if (p.font() != fi.font)
345                         p.setFont(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;
353         }
354         // Draw the cached pixmap.
355         drawPixmap(x + lb, y - mA, pm);
356
357         return textwidth;
358 }
359
360
361 } // namespace frontend
362 } // namespace lyx