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