]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiPainter.cpp
Compil fix.
[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 "ColorCache.h"
17 #include "GuiApplication.h"
18 #include "GuiFontLoader.h"
19 #include "GuiFontMetrics.h"
20 #include "GuiImage.h"
21 #include "qt_helpers.h"
22
23 #include "FontInfo.h"
24 #include "Language.h"
25 #include "LyXRC.h"
26
27 #include "insets/Inset.h"
28
29 #include "support/lassert.h"
30 #include "support/debug.h"
31
32 #include <QPixmapCache>
33 #include <QTextLayout>
34
35 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
36 // drawing text. This is especially useful for older PPC/Mac systems.
37 #if defined(Q_WS_X11)
38 #define USE_PIXMAP_CACHE 0
39 #else
40 #define USE_PIXMAP_CACHE 1
41 #endif
42
43 using namespace std;
44
45 namespace lyx {
46 namespace frontend {
47
48 GuiPainter::GuiPainter(QPaintDevice * device)
49         : QPainter(device), Painter(),
50           use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
51 {
52         // new QPainter has default QPen:
53         current_color_ = guiApp->colorCache().get(Color_black);
54         current_ls_ = line_solid;
55         current_lw_ = line_thin;
56 }
57
58
59 GuiPainter::~GuiPainter()
60 {
61         QPainter::end();
62         //lyxerr << "GuiPainter::end()" << endl;
63 }
64
65
66 void GuiPainter::setQPainterPen(QColor const & col,
67         Painter::line_style ls, Painter::line_width lw)
68 {
69         if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
70                 return;
71
72         current_color_ = col;
73         current_ls_ = ls;
74         current_lw_ = lw;
75
76         QPen pen = QPainter::pen();
77         pen.setColor(col);
78
79         switch (ls) {
80                 case line_solid: pen.setStyle(Qt::SolidLine); break;
81                 case line_onoffdash: pen.setStyle(Qt::DotLine); break;
82         }
83
84         switch (lw) {
85                 case line_thin: pen.setWidth(0); break;
86                 case line_thick: pen.setWidth(3); break;
87         }
88
89         setPen(pen);
90 }
91
92
93 QString GuiPainter::generateStringSignature(QString const & str, FontInfo const & f)
94 {
95         QString sig = str;
96         sig.append(QChar(static_cast<short>(f.family())));
97         sig.append(QChar(static_cast<short>(f.series())));
98         sig.append(QChar(static_cast<short>(f.realShape())));
99         sig.append(QChar(static_cast<short>(f.size())));
100         sig.append(QChar(static_cast<short>(f.color())));
101         if (!monochrome_min_.empty()) {
102                 QColor const & min = monochrome_min_.top();
103                 QColor const & max = monochrome_max_.top();
104                 sig.append(QChar(static_cast<short>(min.red())));
105                 sig.append(QChar(static_cast<short>(min.green())));
106                 sig.append(QChar(static_cast<short>(min.blue())));
107                 sig.append(QChar(static_cast<short>(max.red())));
108                 sig.append(QChar(static_cast<short>(max.green())));
109                 sig.append(QChar(static_cast<short>(max.blue())));
110         }
111         return sig;
112 }
113
114
115 QColor GuiPainter::computeColor(ColorCode col)
116 {
117         return filterColor(guiApp->colorCache().get(col));
118 }
119
120
121 QColor GuiPainter::filterColor(QColor const & col)
122 {
123         if (monochrome_min_.empty())
124                 return col;
125
126         // map into [min,max] interval
127         QColor const & min = monochrome_min_.top();
128         QColor const & max = monochrome_max_.top();
129                         
130         qreal v = col.valueF();
131         v *= v; // make it a bit steeper (i.e. darker)
132                 
133         qreal minr, ming, minb;
134         qreal maxr, maxg, maxb;
135         min.getRgbF(&minr, &ming, &minb);
136         max.getRgbF(&maxr, &maxg, &maxb);
137                         
138         QColor c;
139         c.setRgbF(
140                 v * (minr - maxr) + maxr,
141                 v * (ming - maxg) + maxg,
142                 v * (minb - maxb) + maxb);
143         return c;
144 }
145
146
147 void GuiPainter::enterMonochromeMode(ColorCode const & min, ColorCode const & max)
148 {
149         QColor qmin = filterColor(guiApp->colorCache().get(min));
150         QColor qmax = filterColor(guiApp->colorCache().get(max));
151         monochrome_min_.push(qmin);
152         monochrome_max_.push(qmax);
153 }
154
155
156 void GuiPainter::leaveMonochromeMode()
157 {
158         LASSERT(!monochrome_min_.empty(), /**/);
159         monochrome_min_.pop();
160         monochrome_max_.pop();
161 }
162
163
164 void GuiPainter::point(int x, int y, ColorCode col)
165 {
166         if (!isDrawingEnabled())
167                 return;
168
169         setQPainterPen(computeColor(col));
170         drawPoint(x, y);
171 }
172
173
174 void GuiPainter::line(int x1, int y1, int x2, int y2,
175         ColorCode col,
176         line_style ls,
177         line_width lw)
178 {
179         if (!isDrawingEnabled())
180                 return;
181
182         setQPainterPen(computeColor(col), ls, lw);
183         bool const do_antialiasing = renderHints() & TextAntialiasing
184                 && x1 != x2 && y1 != y2;
185         setRenderHint(Antialiasing, do_antialiasing);
186         drawLine(x1, y1, x2, y2);
187         setRenderHint(Antialiasing, false);
188 }
189
190
191 void GuiPainter::lines(int const * xp, int const * yp, int np,
192         ColorCode col,
193         line_style ls,
194         line_width lw)
195 {
196         if (!isDrawingEnabled())
197                 return;
198
199         // double the size if needed
200         static QVector<QPoint> points(32);
201         if (np > points.size())
202                 points.resize(2 * np);
203
204         bool antialias = false;
205         for (int i = 0; i < np; ++i) {
206                 points[i].setX(xp[i]);
207                 points[i].setY(yp[i]);
208                 if (i != 0)
209                         antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
210         }
211         setQPainterPen(computeColor(col), ls, lw);
212         bool const text_is_antialiased = renderHints() & TextAntialiasing;
213         setRenderHint(Antialiasing, antialias && text_is_antialiased);
214         drawPolyline(points.data(), np);
215         setRenderHint(Antialiasing, false);
216 }
217
218
219 void GuiPainter::rectangle(int x, int y, int w, int h,
220         ColorCode col,
221         line_style ls,
222         line_width lw)
223 {
224         if (!isDrawingEnabled())
225                 return;
226
227         setQPainterPen(computeColor(col), ls, lw);
228         drawRect(x, y, w, h);
229 }
230
231
232 void GuiPainter::fillRectangle(int x, int y, int w, int h, ColorCode col)
233 {
234         fillRect(x, y, w, h, guiApp->colorCache().get(col));
235 }
236
237
238 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
239         int a1, int a2, ColorCode col)
240 {
241         if (!isDrawingEnabled())
242                 return;
243
244         // LyX usings 1/64ths degree, Qt usings 1/16th
245         setQPainterPen(computeColor(col));
246         bool const do_antialiasing = renderHints() & TextAntialiasing;
247         setRenderHint(Antialiasing, do_antialiasing);
248         drawArc(x, y, w, h, a1 / 4, a2 / 4);
249         setRenderHint(Antialiasing, false);
250 }
251
252
253 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
254 {
255         graphics::GuiImage const & qlimage =
256                 static_cast<graphics::GuiImage const &>(i);
257
258         fillRectangle(x, y, w, h, Color_graphicsbg);
259
260         if (!isDrawingEnabled())
261                 return;
262
263         drawImage(x, y, qlimage.qimage(), 0, 0, w, h);
264 }
265
266
267 int GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
268 {
269         docstring s(1, c);
270         return text(x, y, s, f);
271 }
272
273
274 int GuiPainter::smallCapsText(int x, int y,
275         QString const & s, FontInfo const & f)
276 {
277         FontInfo smallfont(f);
278         smallfont.decSize().decSize().setShape(UP_SHAPE);
279
280         QFont const & qfont = getFont(f);
281         QFont const & qsmallfont = getFont(smallfont);
282
283         setQPainterPen(computeColor(f.realColor()));
284         int textwidth = 0;
285         size_t const ls = s.length();
286         for (unsigned int i = 0; i < ls; ++i) {
287                 QChar const c = s[i].toUpper();
288                 if (c != s.at(i)) {
289                         setFont(qsmallfont);
290                 } else {
291                         setFont(qfont);
292                 }
293                 if (isDrawingEnabled())
294                         drawText(x + textwidth, y, c);
295                 textwidth += fontMetrics().width(c);
296         }
297         return textwidth;
298 }
299
300
301 int GuiPainter::text(int x, int y, docstring const & s,
302                 FontInfo const & f)
303 {
304         if (s.empty())
305                 return 0;
306
307         /* Caution: The following ucs4 to QString conversions work for symbol fonts
308         only because they are no real conversions but simple casts in reality.
309         When we want to draw a symbol or calculate the metrics we pass the position
310         of the symbol in the font (as given in lib/symbols) as a char_type to the
311         frontend. This is just wrong, because the symbol is no UCS4 character at
312         all. You can think of this number as the code point of the symbol in a
313         custom symbol encoding. It works because this char_type is lateron again
314         interpreted as a position in the font again.
315         The correct solution would be to have extra functions for symbols, but that
316         would require to duplicate a lot of frontend and mathed support code.
317         */
318         QString str = toqstr(s);
319
320 #if 0
321         // HACK: QT3 refuses to show single compose characters
322         //       Still needed with Qt4?
323         if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
324                 str = ' ' + str;
325 #endif
326
327         QFont const & ff = getFont(f); 
328         GuiFontMetrics const & fm = getFontMetrics(f); 
329
330         int textwidth;
331
332         if (f.realShape() == SMALLCAPS_SHAPE) {
333                 textwidth = smallCapsText(x, y, str, f);
334                 if (f.underbar() == FONT_ON)
335                         underline(f, x, y, textwidth);
336                 return textwidth;
337         }
338
339         // Here we use the font width cache instead of
340         //   textwidth = fontMetrics().width(str);
341         // because the above is awfully expensive on MacOSX
342         textwidth = fm.width(s);
343         if (f.underbar() == FONT_ON)
344                 underline(f, x, y, textwidth);
345
346         if (!isDrawingEnabled())
347                 return textwidth;
348
349         // Qt4 does not display a glyph whose codepoint is the
350         // same as that of a soft-hyphen (0x00ad), unless it
351         // occurs at a line-break. As a kludge, we force Qt to
352         // render this glyph using a one-column line.
353         if (s.size() == 1 && str[0].unicode() == 0x00ad) {
354                 setQPainterPen(computeColor(f.realColor()));
355                 QTextLayout adsymbol(str);
356                 adsymbol.setFont(ff);
357                 adsymbol.beginLayout();
358                 QTextLine line = adsymbol.createLine();
359                 line.setNumColumns(1);
360                 line.setPosition(QPointF(0, -line.ascent()));
361                 adsymbol.endLayout();
362                 line.draw(this, QPointF(x, y));
363                 return textwidth;
364         }
365
366         if (!use_pixmap_cache_) {
367                 // don't use the pixmap cache,
368                 // draw directly onto the painting device
369                 setQPainterPen(computeColor(f.realColor()));
370                 if (font() != ff)
371                         setFont(ff);
372                 // We need to draw the text as LTR as we use our own bidi code.
373                 setLayoutDirection(Qt::LeftToRight);
374                 drawText(x, y, str);
375                 //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
376                 //      << " at " << x << "," << y);
377                 return textwidth;
378         }
379
380         QPixmap pm;
381         QString key = generateStringSignature(str, f);
382         // Warning: Left bearing is in general negative! Only the case
383         // where left bearing is negative is of interest WRT the the 
384         // pixmap width and the text x-position.
385         // Only the left bearing of the first character is important
386         // as we always write from left to right, even for
387         // right-to-left languages.
388         int const lb = min(fm.lbearing(s[0]), 0);
389         int const mA = fm.maxAscent();
390         if (!QPixmapCache::find(key, pm)) {
391                 // Only the right bearing of the last character is
392                 // important as we always write from left to right,
393                 // even for right-to-left languages.
394                 int const rb = fm.rbearing(s[s.size()-1]);
395                 int const w = textwidth + rb - lb;
396                 int const mD = fm.maxDescent();
397                 int const h = mA + mD;
398                 pm = QPixmap(w, h);
399                 pm.fill(Qt::transparent);
400                 GuiPainter p(&pm);
401                 p.setQPainterPen(computeColor(f.realColor()));
402                 if (p.font() != ff)
403                         p.setFont(ff);
404                 // We need to draw the text as LTR as we use our own bidi code.
405                 p.setLayoutDirection(Qt::LeftToRight);
406                 p.drawText(-lb, mA, str);
407                 QPixmapCache::insert(key, pm);
408                 //LYXERR(Debug::PAINTING, "h=" << h << "  mA=" << mA << "  mD=" << mD
409                 //      << "  w=" << w << "  lb=" << lb << "  tw=" << textwidth 
410                 //      << "  rb=" << rb);
411         }
412         // Draw the cached pixmap.
413         drawPixmap(x + lb, y - mA, pm);
414
415         return textwidth;
416 }
417
418
419 static int max(int a, int b) { return a > b ? a : b; }
420
421
422 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
423 {
424         if (mouseHover)
425                 fillRectangle(x, y, w, h, Color_buttonhoverbg);
426         else
427                 fillRectangle(x, y, w, h, Color_buttonbg);
428         buttonFrame(x, y, w, h);
429 }
430
431
432 void GuiPainter::buttonFrame(int x, int y, int w, int h)
433 {
434         line(x, y, x, y + h - 1, Color_buttonframe);
435         line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
436         line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
437         line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
438 }
439
440
441 void GuiPainter::rectText(int x, int y, docstring const & str,
442         FontInfo const & font, ColorCode back, ColorCode frame)
443 {
444         int width;
445         int ascent;
446         int descent;
447
448         FontMetrics const & fm = theFontMetrics(font);
449         fm.rectText(str, width, ascent, descent);
450
451         if (back != Color_none)
452                 fillRectangle(x + 1, y - ascent + 1, width - 1,
453                               ascent + descent - 1, back);
454
455         if (frame != Color_none)
456                 rectangle(x, y - ascent, width, ascent + descent, frame);
457
458         text(x + 3, y, str, font);
459 }
460
461
462 void GuiPainter::buttonText(int x, int y, docstring const & str,
463         FontInfo const & font, bool mouseHover)
464 {
465         int width;
466         int ascent;
467         int descent;
468
469         FontMetrics const & fm = theFontMetrics(font);
470         fm.buttonText(str, width, ascent, descent);
471
472         static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
473
474         button(x + d, y - ascent, width - d, descent + ascent, mouseHover);
475         text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
476 }
477
478
479 int GuiPainter::preeditText(int x, int y, char_type c,
480         FontInfo const & font, preedit_style style)
481 {
482         FontInfo temp_font = font;
483         FontMetrics const & fm = theFontMetrics(font);
484         int ascent = fm.maxAscent();
485         int descent = fm.maxDescent();
486         int height = ascent + descent;
487         int width = fm.width(c);
488
489         switch (style) {
490                 case preedit_default:
491                         // default unselecting mode.
492                         fillRectangle(x, y - height + 1, width, height, Color_background);
493                         dashedUnderline(font, x, y - descent + 1, width);
494                         break;
495                 case preedit_selecting:
496                         // We are in selecting mode: white text on black background.
497                         fillRectangle(x, y - height + 1, width, height, Color_black);
498                         temp_font.setColor(Color_white);
499                         break;
500                 case preedit_cursor:
501                         // The character comes with a cursor.
502                         fillRectangle(x, y - height + 1, width, height, Color_background);
503                         underline(font, x, y - descent + 1, width);
504                         break;
505         }
506         text(x, y - descent + 1, c, temp_font);
507
508         return width;
509 }
510
511
512 void GuiPainter::underline(FontInfo const & f, int x, int y, int width)
513 {
514         FontMetrics const & fm = theFontMetrics(f);
515
516         int const below = max(fm.maxDescent() / 2, 2);
517         int const height = max((fm.maxDescent() / 4) - 1, 1);
518
519         if (height < 2)
520                 line(x, y + below, x + width, y + below, f.color());
521         else
522                 fillRectangle(x, y + below, width, below + height, f.color());
523 }
524
525
526 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
527 {
528         FontMetrics const & fm = theFontMetrics(f);
529
530         int const below = max(fm.maxDescent() / 2, 2);
531         int height = max((fm.maxDescent() / 4) - 1, 1);
532
533         if (height >= 2)
534                 height += below;
535
536         for (int n = 0; n != height; ++n)
537                 line(x, y + below + n, x + width, y + below + n, f.color(), line_onoffdash);
538 }
539
540 } // namespace frontend
541 } // namespace lyx