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