]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/QLPainter.cpp
Streamlining CollapseStatus stuff
[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 this to one for enabling the use of a Pixmap cache when drawing
33 // text. This is especially useful for older PPC/Mac systems.
34 #define USE_PIXMAP_CACHE 1
35
36 using std::endl;
37 using std::string;
38
39 namespace lyx {
40 namespace frontend {
41
42 namespace {
43
44 bool const usePixmapCache = USE_PIXMAP_CACHE;
45
46 QString generateStringSignature(QString const & str, Font const & f)
47 {
48         QString sig = str;
49         sig.append(QChar(static_cast<short>(f.family())));
50         sig.append(QChar(static_cast<short>(f.series())));
51         sig.append(QChar(static_cast<short>(f.realShape())));
52         sig.append(QChar(static_cast<short>(f.size())));
53         sig.append(QChar(static_cast<short>(f.color())));
54         return sig;
55 }
56
57 } // anon namespace
58
59 QLPainter::QLPainter(QPaintDevice * device)
60         : QPainter(device), Painter()
61 {
62         // new QPainter has default QPen:
63         current_color_ = Color::black;
64         current_ls_ = line_solid;
65         current_lw_ = line_thin;
66 }
67
68
69 QLPainter::~QLPainter()
70 {
71         QPainter::end();
72         //lyxerr << "QLPainter::end()" << endl;
73 }
74
75
76 void QLPainter::setQPainterPen(Color_color col,
77         Painter::line_style ls, Painter::line_width lw)
78 {
79         if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
80                 return;
81
82         current_color_ = col;
83         current_ls_ = ls;
84         current_lw_ = lw;
85
86         QPen pen = QPainter::pen();
87
88         pen.setColor(guiApp->colorCache().get(col));
89
90         switch (ls) {
91                 case line_solid: pen.setStyle(Qt::SolidLine); break;
92                 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
93         }
94
95         switch (lw) {
96                 case line_thin: pen.setWidth(0); break;
97                 case line_thick: pen.setWidth(3); break;
98         }
99
100         setPen(pen);
101 }
102
103
104 void QLPainter::point(int x, int y, Color_color col)
105 {
106         if (!isDrawingEnabled())
107                 return;
108
109         setQPainterPen(col);
110         drawPoint(x, y);
111 }
112
113
114 void QLPainter::line(int x1, int y1, int x2, int y2,
115         Color_color col,
116         line_style ls,
117         line_width lw)
118 {
119         if (!isDrawingEnabled())
120                 return;
121
122         setQPainterPen(col, ls, lw);
123         bool const do_antialiasing = renderHints() & TextAntialiasing
124                 && x1 != x2 && y1 != y2;
125         setRenderHint(Antialiasing, do_antialiasing);
126         drawLine(x1, y1, x2, y2);
127         setRenderHint(Antialiasing, false);
128 }
129
130
131 void QLPainter::lines(int const * xp, int const * yp, int np,
132         Color_color col,
133         line_style ls,
134         line_width lw)
135 {
136         if (!isDrawingEnabled())
137                 return;
138
139         // double the size if needed
140         static QVector<QPoint> points(32);
141         if (np > points.size())
142                 points.resize(2 * np);
143
144         bool antialias = false;
145         for (int i = 0; i < np; ++i) {
146                 points[i].setX(xp[i]);
147                 points[i].setY(yp[i]);
148                 if (i != 0)
149                         antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
150         }
151         setQPainterPen(col, ls, lw);
152         bool const text_is_antialiased = renderHints() & TextAntialiasing;
153         setRenderHint(Antialiasing, antialias && text_is_antialiased);
154         drawPolyline(points.data(), np);
155         setRenderHint(Antialiasing, false);
156 }
157
158
159 void QLPainter::rectangle(int x, int y, int w, int h,
160         Color_color col,
161         line_style ls,
162         line_width lw)
163 {
164         if (!isDrawingEnabled())
165                 return;
166
167         setQPainterPen(col, ls, lw);
168         drawRect(x, y, w, h);
169 }
170
171
172 void QLPainter::fillRectangle(int x, int y, int w, int h, Color_color col)
173 {
174         fillRect(x, y, w, h, guiApp->colorCache().get(col));
175 }
176
177
178 void QLPainter::arc(int x, int y, unsigned int w, unsigned int h,
179         int a1, int a2, Color_color col)
180 {
181         if (!isDrawingEnabled())
182                 return;
183
184         // LyX usings 1/64ths degree, Qt usings 1/16th
185         setQPainterPen(col);
186         bool const do_antialiasing = renderHints() & TextAntialiasing;
187         setRenderHint(Antialiasing, do_antialiasing);
188         drawArc(x, y, w, h, a1 / 4, a2 / 4);
189         setRenderHint(Antialiasing, false);
190 }
191
192
193 void QLPainter::image(int x, int y, int w, int h, graphics::Image const & i)
194 {
195         graphics::QLImage const & qlimage =
196                 static_cast<graphics::QLImage const &>(i);
197
198         fillRectangle(x, y, w, h, Color::graphicsbg);
199
200         if (!isDrawingEnabled())
201                 return;
202
203         drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
204 }
205
206
207 int QLPainter::text(int x, int y, char_type c, Font const & f)
208 {
209         docstring s(1, c);
210         return text(x, y, s, f);
211 }
212
213
214 int QLPainter::smallCapsText(int x, int y,
215         QString const & s, Font const & f)
216 {
217         Font smallfont(f);
218         smallfont.decSize().decSize().setShape(Font::UP_SHAPE);
219
220         QFont const & qfont = guiApp->guiFontLoader().get(f);
221         QFont const & qsmallfont = guiApp->guiFontLoader().get(smallfont);
222
223         setQPainterPen(f.realColor());
224         int textwidth = 0;
225         size_t const ls = s.length();
226         for (unsigned int i = 0; i < ls; ++i) {
227                 QChar const c = s[i].toUpper();
228                 if (c != s.at(i)) {
229                         setFont(qsmallfont);
230                 } else {
231                         setFont(qfont);
232                 }
233                 if (isDrawingEnabled())
234                         drawText(x + textwidth, y, c);
235                 textwidth += fontMetrics().width(c);
236         }
237         return textwidth;
238 }
239
240
241 int QLPainter::text(int x, int y, docstring const & s,
242                 Font const & f)
243 {
244         /* Caution: The following ucs4 to QString conversions work for symbol fonts
245         only because they are no real conversions but simple casts in reality.
246         When we want to draw a symbol or calculate the metrics we pass the position
247         of the symbol in the font (as given in lib/symbols) as a char_type to the
248         frontend. This is just wrong, because the symbol is no UCS4 character at
249         all. You can think of this number as the code point of the symbol in a
250         custom symbol encoding. It works because this char_type is lateron again
251         interpreted as a position in the font again.
252         The correct solution would be to have extra functions for symbols, but that
253         would require to duplicate a lot of frontend and mathed support code.
254         */
255         QString str = toqstr(s);
256
257 #if 0
258         // HACK: QT3 refuses to show single compose characters
259         //       Still needed with Qt4?
260         if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
261                 str = ' ' + str;
262 #endif
263
264         QLFontInfo & fi = guiApp->guiFontLoader().fontinfo(f);
265
266         int textwidth;
267
268         if (f.realShape() == Font::SMALLCAPS_SHAPE) {
269                 textwidth = smallCapsText(x, y, str, f);
270                 if (f.underbar() == Font::ON)
271                         underline(f, x, y, textwidth);
272                 return textwidth;
273         }
274
275         // Here we use the font width cache instead of
276         //   textwidth = fontMetrics().width(str);
277         // because the above is awfully expensive on MacOSX
278         textwidth = fi.metrics->width(s);
279         if (f.underbar() == Font::ON)
280                 underline(f, x, y, textwidth);
281
282         if (!isDrawingEnabled())
283                 return textwidth;
284
285         // Qt4 does not display a glyph whose codepoint is the
286         // same as that of a soft-hyphen (0x00ad), unless it
287         // occurs at a line-break. As a kludge, we force Qt to
288         // render this glyph using a one-column line.
289         if (s.size() == 1 && str[0].unicode() == 0x00ad) {
290                 setQPainterPen(f.realColor());
291                 QTextLayout adsymbol(str);
292                 adsymbol.setFont(fi.font);
293                 adsymbol.beginLayout();
294                 QTextLine line = adsymbol.createLine();
295                 line.setNumColumns(1);
296                 line.setPosition(QPointF(0, -line.ascent()));
297                 adsymbol.endLayout();
298                 line.draw(this, QPointF(x, y));
299                 return textwidth;
300         }
301
302         if (!usePixmapCache) {
303                 // don't use the pixmap cache,
304                 // draw directly onto the painting device
305                 setQPainterPen(f.realColor());
306                 if (font() != fi.font)
307                         setFont(fi.font);
308                 // We need to draw the text as LTR as we use our own bidi code.
309                 setLayoutDirection(Qt::LeftToRight);
310                 // We need to draw the text as LTR as we use our own bidi code.
311                 setLayoutDirection(Qt::LeftToRight);
312                 drawText(x, y, str);
313                 LYXERR(Debug::PAINTING) << "draw " << std::string(str.toUtf8())
314                         << " at " << x << "," << y << std::endl;
315                 return textwidth;
316         }
317
318         QPixmap pm;
319         QString key = generateStringSignature(str, f);
320         // Warning: Left bearing is in general negative! Only the case
321         // where left bearing is negative is of interest WRT the the 
322         // pixmap width and the text x-position.
323         // Only the left bearing of the first character is important
324         // as we always write from left to right, even for
325         // right-to-left languages.
326         int const lb = std::min(fi.metrics->lbearing(s[0]), 0);
327         int const mA = fi.metrics->maxAscent();
328         if (!QPixmapCache::find(key, pm)) {
329                 // Only the right bearing of the last character is
330                 // important as we always write from left to right,
331                 // even for right-to-left languages.
332                 int const rb = fi.metrics->rbearing(s[s.size()-1]);
333                 int const w = textwidth + rb - lb;
334                 int const mD = fi.metrics->maxDescent();
335                 int const h = mA + mD;
336                 pm = QPixmap(w, h);
337                 pm.fill(Qt::transparent);
338                 QLPainter p(&pm);
339                 p.setQPainterPen(f.realColor());
340                 if (p.font() != fi.font)
341                         p.setFont(fi.font);
342                 // We need to draw the text as LTR as we use our own bidi code.
343                 p.setLayoutDirection(Qt::LeftToRight);
344                 p.drawText(-lb, mA, str);
345                 QPixmapCache::insert(key, pm);
346                 LYXERR(Debug::PAINTING) << "h=" << h << "  mA=" << mA << "  mD=" << mD
347                         << "  w=" << w << "  lb=" << lb << "  tw=" << textwidth 
348                         << "  rb=" << rb << endl;
349         }
350         // Draw the cached pixmap.
351         drawPixmap(x + lb, y - mA, pm);
352
353         return textwidth;
354 }
355
356
357 } // namespace frontend
358 } // namespace lyx