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