]> git.lyx.org Git - lyx.git/blob - src/frontends/qt4/GuiPainter.cpp
rename buffer parameter math_number_before to math_numbering_side
[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 "Font.h"
24 #include "Language.h"
25 #include "LyXRC.h"
26
27 #include "insets/Inset.h"
28
29 #include "support/debug.h"
30 #include "support/lassert.h"
31 #include "support/lyxlib.h"
32
33 #include <algorithm>
34
35 #include <QPixmapCache>
36 #include <QTextLayout>
37
38 // Set USE_PIXMAP_CACHE to 1 for enabling the use of a Pixmap cache when
39 // drawing text. This is especially useful for older PPC/Mac systems.
40 #if defined(Q_WS_X11) || defined(QPA_XCB)
41 #define USE_PIXMAP_CACHE 0
42 #else
43 #define USE_PIXMAP_CACHE 1
44 #endif
45
46 using namespace std;
47 using namespace lyx::support;
48
49 namespace lyx {
50 namespace frontend {
51
52 const int Painter::thin_line = 1;
53
54 GuiPainter::GuiPainter(QPaintDevice * device, double pixel_ratio)
55         : QPainter(device), Painter(pixel_ratio),
56           use_pixmap_cache_(lyxrc.use_pixmap_cache && USE_PIXMAP_CACHE)
57 {
58         // new QPainter has default QPen:
59         current_color_ = guiApp->colorCache().get(Color_black);
60         current_ls_ = line_solid;
61         current_lw_ = thin_line;
62 }
63
64
65 GuiPainter::~GuiPainter()
66 {
67         QPainter::end();
68         //lyxerr << "GuiPainter::end()" << endl;
69 }
70
71
72 void GuiPainter::setQPainterPen(QColor const & col,
73         Painter::line_style ls, int lw)
74 {
75         if (col == current_color_ && ls == current_ls_ && lw == current_lw_)
76                 return;
77
78         current_color_ = col;
79         current_ls_ = ls;
80         current_lw_ = lw;
81
82         QPen pen = QPainter::pen();
83         pen.setColor(col);
84
85         switch (ls) {
86         case line_solid:
87         case line_solid_aliased:
88                 pen.setStyle(Qt::SolidLine); break;
89         case line_onoffdash:
90                 pen.setStyle(Qt::DotLine); break;
91         }
92
93         pen.setWidth(lw);
94
95         setPen(pen);
96 }
97
98
99 QString GuiPainter::generateStringSignature(QString const & str,
100                                             FontInfo const & f,
101                                             double wordspacing)
102 {
103         QString sig = str;
104         sig.append(QChar(static_cast<short>(f.family())));
105         sig.append(QChar(static_cast<short>(f.series())));
106         sig.append(QChar(static_cast<short>(f.realShape())));
107         sig.append(QChar(static_cast<short>(f.size())));
108         Color const & color = f.realColor();
109         sig.append(QChar(static_cast<short>(color.baseColor)));
110         sig.append(QChar(static_cast<short>(color.mergeColor)));
111         sig.append(QString::number(wordspacing));
112         if (!monochrome_min_.empty()) {
113                 QColor const & min = monochrome_min_.top();
114                 QColor const & max = monochrome_max_.top();
115                 sig.append(QChar(static_cast<short>(min.red())));
116                 sig.append(QChar(static_cast<short>(min.green())));
117                 sig.append(QChar(static_cast<short>(min.blue())));
118                 sig.append(QChar(static_cast<short>(max.red())));
119                 sig.append(QChar(static_cast<short>(max.green())));
120                 sig.append(QChar(static_cast<short>(max.blue())));
121         }
122         return sig;
123 }
124
125
126 QColor GuiPainter::computeColor(Color col)
127 {
128         return filterColor(guiApp->colorCache().get(col));
129 }
130
131
132 QColor GuiPainter::filterColor(QColor const & col)
133 {
134         if (monochrome_min_.empty())
135                 return col;
136
137         // map into [min,max] interval
138         QColor const & min = monochrome_min_.top();
139         QColor const & max = monochrome_max_.top();
140                         
141         qreal v = col.valueF();
142         v *= v; // make it a bit steeper (i.e. darker)
143                 
144         qreal minr, ming, minb;
145         qreal maxr, maxg, maxb;
146         min.getRgbF(&minr, &ming, &minb);
147         max.getRgbF(&maxr, &maxg, &maxb);
148                         
149         QColor c;
150         c.setRgbF(
151                 v * (minr - maxr) + maxr,
152                 v * (ming - maxg) + maxg,
153                 v * (minb - maxb) + maxb);
154         return c;
155 }
156
157
158 void GuiPainter::enterMonochromeMode(Color const & min, Color const & max)
159 {
160         QColor qmin = filterColor(guiApp->colorCache().get(min));
161         QColor qmax = filterColor(guiApp->colorCache().get(max));
162         monochrome_min_.push(qmin);
163         monochrome_max_.push(qmax);
164 }
165
166
167 void GuiPainter::leaveMonochromeMode()
168 {
169         LASSERT(!monochrome_min_.empty(), return);
170         monochrome_min_.pop();
171         monochrome_max_.pop();
172 }
173
174
175 void GuiPainter::point(int x, int y, Color col)
176 {
177         if (!isDrawingEnabled())
178                 return;
179
180         setQPainterPen(computeColor(col));
181         drawPoint(x, y);
182 }
183
184
185 void GuiPainter::line(int x1, int y1, int x2, int y2,
186         Color col,
187         line_style ls,
188         int lw)
189 {
190         if (!isDrawingEnabled())
191                 return;
192
193         setQPainterPen(computeColor(col), ls, lw);
194         bool const do_antialiasing = renderHints() & TextAntialiasing
195                 && x1 != x2 && y1 != y2 && ls != line_solid_aliased;
196         setRenderHint(Antialiasing, do_antialiasing);
197         drawLine(x1, y1, x2, y2);
198         setRenderHint(Antialiasing, false);
199 }
200
201
202 void GuiPainter::lines(int const * xp, int const * yp, int np,
203         Color col,
204         fill_style fs,
205         line_style ls,
206         int lw)
207 {
208         if (!isDrawingEnabled())
209                 return;
210
211         // double the size if needed
212         // FIXME THREAD
213         static QVector<QPoint> points(32);
214         if (np > points.size())
215                 points.resize(2 * np);
216
217         // Note: the proper way to not get blurry vertical and horizontal lines is
218         // to add 0.5 to all coordinates.
219         bool antialias = false;
220         for (int i = 0; i < np; ++i) {
221                 points[i].setX(xp[i]);
222                 points[i].setY(yp[i]);
223                 if (i != 0)
224                         antialias |= xp[i-1] != xp[i] && yp[i-1] != yp[i];
225         }
226         QColor const color = computeColor(col);
227         setQPainterPen(color, ls, lw);
228         bool const text_is_antialiased = renderHints() & TextAntialiasing;
229         setRenderHint(Antialiasing,
230                       antialias && text_is_antialiased && ls != line_solid_aliased);
231         if (fs == fill_none) {
232                 drawPolyline(points.data(), np);
233         } else {
234                 QBrush const oldbrush = brush();
235                 setBrush(QBrush(color));
236                 drawPolygon(points.data(), np, fs == fill_oddeven ?
237                             Qt::OddEvenFill : Qt::WindingFill);
238                 setBrush(oldbrush);
239         }
240         setRenderHint(Antialiasing, false);
241 }
242
243
244 void GuiPainter::path(int const * xp, int const * yp,
245         int const * c1x, int const * c1y,
246         int const * c2x, int const * c2y,
247         int np,
248         Color col,
249         fill_style fs,
250         line_style ls,
251         int lw)
252 {
253         if (!isDrawingEnabled())
254                 return;
255
256         QPainterPath bpath;
257         // This is the starting point, so its control points are meaningless
258         bpath.moveTo(xp[0], yp[0]);
259
260         for (int i = 1; i < np; ++i) {
261                 bool line = c1x[i] == xp[i - 1] && c1y[i] == yp[i - 1] &&
262                             c2x[i] == xp[i] && c2y[i] == yp[i];
263                 if (line)
264                         bpath.lineTo(xp[i], yp[i]);
265                 else
266                         bpath.cubicTo(c1x[i], c1y[i],  c2x[i], c2y[i], xp[i], yp[i]);
267         }
268         QColor const color = computeColor(col);
269         setQPainterPen(color, ls, lw);
270         bool const text_is_antialiased = renderHints() & TextAntialiasing;
271         setRenderHint(Antialiasing, text_is_antialiased && ls != line_solid_aliased);
272         drawPath(bpath);
273         if (fs != fill_none)
274                 fillPath(bpath, QBrush(color));
275         setRenderHint(Antialiasing, false);
276 }
277
278
279 void GuiPainter::rectangle(int x, int y, int w, int h,
280         Color col,
281         line_style ls,
282         int lw)
283 {
284         if (!isDrawingEnabled())
285                 return;
286
287         setQPainterPen(computeColor(col), ls, lw);
288         drawRect(x, y, w, h);
289 }
290
291
292 void GuiPainter::fillRectangle(int x, int y, int w, int h, Color col)
293 {
294         if (!isDrawingEnabled())
295                 return;
296
297         fillRect(x, y, w, h, guiApp->colorCache().get(col));
298 }
299
300
301 void GuiPainter::arc(int x, int y, unsigned int w, unsigned int h,
302         int a1, int a2, Color col)
303 {
304         if (!isDrawingEnabled())
305                 return;
306
307         // LyX usings 1/64ths degree, Qt usings 1/16th
308         setQPainterPen(computeColor(col));
309         bool const do_antialiasing = renderHints() & TextAntialiasing;
310         setRenderHint(Antialiasing, do_antialiasing);
311         drawArc(x, y, w, h, a1 / 4, a2 / 4);
312         setRenderHint(Antialiasing, false);
313 }
314
315
316 void GuiPainter::image(int x, int y, int w, int h, graphics::Image const & i)
317 {
318         graphics::GuiImage const & qlimage =
319                 static_cast<graphics::GuiImage const &>(i);
320
321         fillRectangle(x, y, w, h, Color_graphicsbg);
322
323         if (!isDrawingEnabled())
324                 return;
325
326         QImage const image = qlimage.image();
327         QRectF const drect = QRectF(x, y, w, h);
328         QRectF const srect = QRectF(0, 0, image.width(), image.height());
329         // Bilinear filtering is needed on a rare occasion for instant previews when
330         // the user's configuration mixes low-dpi and high-dpi monitors (#10114).
331         // This filter is optimised by qt on pixel-aligned images, so this does not
332         // affect performances in other cases.
333         setRenderHint(SmoothPixmapTransform);
334         drawImage(drect, image, srect);
335         setRenderHint(SmoothPixmapTransform, false);
336 }
337
338
339 void GuiPainter::text(int x, int y, char_type c, FontInfo const & f)
340 {
341         text(x, y, docstring(1, c), f);
342 }
343
344
345 void GuiPainter::text(int x, int y, docstring const & s, FontInfo const & f)
346 {
347         text(x, y, s, f, Auto, 0.0, 0.0);
348 }
349
350
351 void GuiPainter::do_drawText(int x, int y, QString str,
352                              GuiPainter::Direction const dir,
353                              FontInfo const & f, QFont ff)
354 {
355         setQPainterPen(computeColor(f.realColor()));
356         if (font() != ff)
357                 setFont(ff);
358
359          /* In LyX, the character direction is forced by the language.
360           * Therefore, we have to signal that fact to Qt.
361           */
362 #if 1
363         /* Use unicode override characters to enforce drawing direction
364          * Source: http://www.iamcal.com/understanding-bidirectional-text/
365          */
366         if (dir == RtL)
367                 // Right-to-left override: forces to draw text right-to-left
368                 str = QChar(0x202E) + str;
369         else if (dir == LtR)
370                 // Left-to-right override: forces to draw text left-to-right
371                 str =  QChar(0x202D) + str;
372         drawText(x, y, str);
373 #else
374         /* This looks like a cleaner solution, but it has drawbacks
375          * - does not work reliably (Mac OS X, ...)
376          * - it is not really documented
377          * Keep it here for now, in case it can be helpful
378          */
379         //This is much stronger than setLayoutDirection.
380         int flag = 0;
381         if (dir == RtL)
382                 flag = Qt::TextForceRightToLeft;
383         else if (dir == LtR)
384                 flag = Qt::TextForceLeftToRight;
385         drawText(x + ((dir == RtL) ? textwidth : 0), y - fm.maxAscent(), 0, 0,
386                  flag | Qt::TextDontClip,
387                  str);
388 #endif
389 }
390
391
392 void GuiPainter::text(int x, int y, docstring const & s,
393                       FontInfo const & f, Direction const dir,
394                       double const wordspacing, double const tw)
395 {
396         //LYXERR0("text: x=" << x << ", s=" << s);
397         if (s.empty() || !isDrawingEnabled())
398                 return;
399
400         /* Caution: The following ucs4 to QString conversions work for symbol fonts
401         only because they are no real conversions but simple casts in reality.
402         When we want to draw a symbol or calculate the metrics we pass the position
403         of the symbol in the font (as given in lib/symbols) as a char_type to the
404         frontend. This is just wrong, because the symbol is no UCS4 character at
405         all. You can think of this number as the code point of the symbol in a
406         custom symbol encoding. It works because this char_type is later on again
407         interpreted as a position in the font.
408         The correct solution would be to have extra functions for symbols, but that
409         would require to duplicate a lot of frontend and mathed support code.
410         */
411         QString str = toqstr(s);
412
413 #if 0
414         // HACK: QT3 refuses to show single compose characters
415         //       Still needed with Qt4?
416         if (ls == 1 && str[0].unicode() >= 0x05b0 && str[0].unicode() <= 0x05c2)
417                 str = ' ' + str;
418 #endif
419
420         QFont ff = getFont(f);
421         ff.setWordSpacing(wordspacing);
422         GuiFontMetrics const & fm = getFontMetrics(f);
423
424         int textwidth = 0;
425         if (tw == 0.0)
426                 // Note that we have to take in account space stretching (word spacing)
427                 textwidth = fm.width(s) +
428                         static_cast<int>(fm.countExpanders(s) * wordspacing);
429         else
430                 textwidth = static_cast<int>(tw);
431
432         textDecoration(f, x, y, textwidth);
433
434         if (use_pixmap_cache_) {
435                 QPixmap pm;
436                 QString key = generateStringSignature(str, f, wordspacing);
437
438                 // Warning: Left bearing is in general negative! Only the case
439                 // where left bearing is negative is of interest WRT the
440                 // pixmap width and the text x-position.
441                 // Only the left bearing of the first character is important
442                 // as we always write from left to right, even for
443                 // right-to-left languages.
444                 // FIXME: this is probably broken for RTL now that we draw full strings.
445                 // Morover the first/last element is possibly not the right one since the glyph may have changed.
446                 int const lb = min(fm.lbearing(s[0]), 0);
447                 int const mA = fm.maxAscent();
448                 if (QPixmapCache::find(key, pm)) {
449                         // Draw the cached pixmap.
450                         drawPixmap(x + lb, y - mA, pm);
451                         return;
452                 }
453
454                 // Only the right bearing of the last character is
455                 // important as we always write from left to right,
456                 // even for right-to-left languages.
457                 int const rb = fm.rbearing(s[s.size()-1]);
458                 int const w = textwidth + rb - lb;
459                 int const mD = fm.maxDescent();
460                 int const h = mA + mD;
461                 if (w > 0 && h > 0) {
462                         pm = QPixmap(static_cast<int>(pixelRatio() * w),
463                                                  static_cast<int>(pixelRatio() * h));
464 #if QT_VERSION >= 0x050000
465                         pm.setDevicePixelRatio(pixelRatio());
466 #endif
467                         pm.fill(Qt::transparent);
468                         GuiPainter p(&pm, pixelRatio());
469                         p.do_drawText(-lb, mA, str, dir, f, ff);
470                         QPixmapCache::insert(key, pm);
471                         //LYXERR(Debug::PAINTING, "h=" << h << "  mA=" << mA << "  mD=" << mD
472                         //      << "  w=" << w << "  lb=" << lb << "  tw=" << textwidth
473                         //      << "  rb=" << rb);
474
475                         // Draw the new cached pixmap.
476                         drawPixmap(x + lb, y - mA, pm);
477                         //rectangle(x-lb, y-mA, w, h, Color_green);
478                 }
479                 return;
480         }
481
482         // don't use the pixmap cache
483         setQPainterPen(computeColor(f.realColor()));
484         if (dir != Auto) {
485                 shared_ptr<QTextLayout const> ptl =
486                         fm.getTextLayout(s, dir == RtL, wordspacing);
487                 ptl->draw(this, QPointF(x, y - fm.maxAscent()));
488         }
489         else {
490                 if (font() != ff)
491                         setFont(ff);
492                 drawText(x, y, str);
493         }
494         //LYXERR(Debug::PAINTING, "draw " << string(str.toUtf8())
495         //      << " at " << x << "," << y);
496 }
497
498
499 void GuiPainter::text(int x, int y, docstring const & str, Font const & f,
500                       double const wordspacing, double const tw)
501 {
502         text(x, y, str, f.fontInfo(), f.isVisibleRightToLeft() ? RtL : LtR,
503              wordspacing, tw);
504 }
505
506
507 void GuiPainter::text(int x, int y, docstring const & str, Font const & f,
508                       Color other, size_type const from, size_type const to,
509                       double const wordspacing, double const tw)
510 {
511         GuiFontMetrics const & fm = getFontMetrics(f.fontInfo());
512         FontInfo fi = f.fontInfo();
513         Direction const dir = f.isVisibleRightToLeft() ? RtL : LtR;
514
515         // dimensions
516         int const ascent = fm.maxAscent();
517         int const height = fm.maxAscent() + fm.maxDescent();
518         int xmin = fm.pos2x(str, from, dir == RtL, wordspacing);
519         int xmax = fm.pos2x(str, to, dir == RtL, wordspacing);
520         if (xmin > xmax)
521                 swap(xmin, xmax);
522
523         // First the part in other color
524         Color const orig = fi.realColor();
525         fi.setPaintColor(other);
526         QRegion const clip(x + xmin, y - ascent, xmax - xmin, height);
527         setClipRegion(clip);
528         text(x, y, str, fi, dir, wordspacing, tw);
529
530         // Then the part in normal color
531         // Note that in Qt5, it is not possible to use Qt::UniteClip,
532         // therefore QRegion is used.
533         fi.setPaintColor(orig);
534         QRegion region(viewport());
535         setClipRegion(region - clip);
536         text(x, y, str, fi, dir, wordspacing, tw);
537         setClipping(false);
538 }
539
540
541 void GuiPainter::textDecoration(FontInfo const & f, int x, int y, int width)
542 {
543         if (f.underbar() == FONT_ON)
544                 underline(f, x, y, width);
545         if (f.strikeout() == FONT_ON)
546                 strikeoutLine(f, x, y, width);
547         if (f.xout() == FONT_ON)
548                 crossoutLines(f, x, y, width);
549         if (f.uuline() == FONT_ON)
550                 doubleUnderline(f, x, y, width);
551         if (f.uwave() == FONT_ON)
552                 // f.color() doesn't work on some circumstances
553                 wavyHorizontalLine(x, y, width,  f.realColor().baseColor);
554 }
555
556
557 static int max(int a, int b) { return a > b ? a : b; }
558
559
560 void GuiPainter::button(int x, int y, int w, int h, bool mouseHover)
561 {
562         if (mouseHover)
563                 fillRectangle(x, y, w, h, Color_buttonhoverbg);
564         else
565                 fillRectangle(x, y, w, h, Color_buttonbg);
566         buttonFrame(x, y, w, h);
567 }
568
569
570 void GuiPainter::buttonFrame(int x, int y, int w, int h)
571 {
572         line(x, y, x, y + h - 1, Color_buttonframe);
573         line(x - 1 + w, y, x - 1 + w, y + h - 1, Color_buttonframe);
574         line(x, y - 1, x - 1 + w, y - 1, Color_buttonframe);
575         line(x, y + h - 1, x - 1 + w, y + h - 1, Color_buttonframe);
576 }
577
578
579 void GuiPainter::rectText(int x, int y, docstring const & str,
580         FontInfo const & font, Color back, Color frame)
581 {
582         int width;
583         int ascent;
584         int descent;
585
586         FontMetrics const & fm = theFontMetrics(font);
587         fm.rectText(str, width, ascent, descent);
588
589         if (back != Color_none)
590                 fillRectangle(x + 1, y - ascent + 1, width - 1,
591                               ascent + descent - 1, back);
592
593         if (frame != Color_none)
594                 rectangle(x, y - ascent, width, ascent + descent, frame);
595
596         text(x + 3, y, str, font);
597 }
598
599
600 void GuiPainter::buttonText(int x, int y, docstring const & str,
601         FontInfo const & font, bool mouseHover)
602 {
603         int width;
604         int ascent;
605         int descent;
606
607         FontMetrics const & fm = theFontMetrics(font);
608         fm.buttonText(str, width, ascent, descent);
609
610         static int const d = Inset::TEXT_TO_INSET_OFFSET / 2;
611
612         button(x + d, y - ascent, width - Inset::TEXT_TO_INSET_OFFSET, descent + ascent, mouseHover);
613         text(x + Inset::TEXT_TO_INSET_OFFSET, y, str, font);
614 }
615
616
617 int GuiPainter::preeditText(int x, int y, char_type c,
618         FontInfo const & font, preedit_style style)
619 {
620         FontInfo temp_font = font;
621         FontMetrics const & fm = theFontMetrics(font);
622         int ascent = fm.maxAscent();
623         int descent = fm.maxDescent();
624         int height = ascent + descent;
625         int width = fm.width(c);
626
627         switch (style) {
628                 case preedit_default:
629                         // default unselecting mode.
630                         fillRectangle(x, y - height + 1, width, height, Color_background);
631                         dashedUnderline(font, x, y - descent + 1, width);
632                         break;
633                 case preedit_selecting:
634                         // We are in selecting mode: white text on black background.
635                         fillRectangle(x, y - height + 1, width, height, Color_black);
636                         temp_font.setColor(Color_white);
637                         break;
638                 case preedit_cursor:
639                         // The character comes with a cursor.
640                         fillRectangle(x, y - height + 1, width, height, Color_background);
641                         underline(font, x, y - descent + 1, width);
642                         break;
643         }
644         text(x, y - descent + 1, c, temp_font);
645
646         return width;
647 }
648
649
650 void GuiPainter::underline(FontInfo const & f, int x, int y, int width,
651                            line_style ls)
652 {
653         FontMetrics const & fm = theFontMetrics(f);
654         int const pos = fm.underlinePos();
655
656         line(x, y + pos, x + width, y + pos,
657              f.realColor(), ls, fm.lineWidth());
658 }
659
660
661 void GuiPainter::strikeoutLine(FontInfo const & f, int x, int y, int width)
662 {
663         FontMetrics const & fm = theFontMetrics(f);
664         int const pos = fm.strikeoutPos();
665
666         line(x, y - pos, x + width, y - pos,
667              f.realColor(), line_solid, fm.lineWidth());
668 }
669
670
671 void GuiPainter::crossoutLines(FontInfo const & f, int x, int y, int width)
672 {
673         FontInfo tmpf = f;
674         tmpf.setXout(FONT_OFF);
675
676         // the definition of \xout in ulem.sty is
677     //  \def\xout{\bgroup \markoverwith{\hbox to.35em{\hss/\hss}}\ULon}
678         // Let's mimick it somewhat.
679         double offset = max(0.35 * theFontMetrics(tmpf).em(), 1);
680         for (int i = 0 ; i < iround(width / offset) ; ++i)
681                 text(x + iround(i * offset), y, '/', tmpf);
682 }
683
684
685 void GuiPainter::doubleUnderline(FontInfo const & f, int x, int y, int width)
686 {
687         FontMetrics const & fm = theFontMetrics(f);
688         int const pos1 = fm.underlinePos() + fm.lineWidth();
689         int const pos2 = fm.underlinePos() - fm.lineWidth() + 1;
690
691         line(x, y + pos1, x + width, y + pos1,
692                  f.realColor(), line_solid, fm.lineWidth());
693         line(x, y + pos2, x + width, y + pos2,
694                  f.realColor(), line_solid, fm.lineWidth());
695 }
696
697
698 void GuiPainter::dashedUnderline(FontInfo const & f, int x, int y, int width)
699 {
700         FontMetrics const & fm = theFontMetrics(f);
701
702         int const below = max(fm.maxDescent() / 2, 2);
703         int height = max((fm.maxDescent() / 4) - 1, 1);
704
705         if (height >= 2)
706                 height += below;
707
708         for (int n = 0; n != height; ++n)
709                 line(x, y + below + n, x + width, y + below + n, f.realColor(), line_onoffdash);
710 }
711
712
713 void GuiPainter::wavyHorizontalLine(int x, int y, int width, ColorCode col)
714 {
715         setQPainterPen(computeColor(col));
716         int const step = 2;
717         int const xend = x + width;
718         int height = 1;
719         //FIXME: I am not sure if Antialiasing gives the best effect.
720         //setRenderHint(Antialiasing, true);
721         while (x < xend) {
722                 height = - height;
723                 drawLine(x, y - height, x + step, y + height);
724                 x += step;
725                 drawLine(x, y + height, x + step/2, y + height);
726                 x += step/2;
727         }
728         //setRenderHint(Antialiasing, false);
729 }
730
731 } // namespace frontend
732 } // namespace lyx