]> git.lyx.org Git - lyx.git/blob - src/rowpainter.C
use std::advance and std::distance instead of home-grown versions
[lyx.git] / src / rowpainter.C
1 /**
2  * \file rowpainter.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author various
7  * \author John Levon
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "rowpainter.h"
15
16 #include "buffer.h"
17 #include "cursor.h"
18 #include "debug.h"
19 #include "bufferparams.h"
20 #include "BufferView.h"
21 #include "encoding.h"
22 #include "gettext.h"
23 #include "language.h"
24 #include "LColor.h"
25 #include "lyxrc.h"
26 #include "lyxrow.h"
27 #include "lyxrow_funcs.h"
28 #include "metricsinfo.h"
29 #include "paragraph.h"
30 #include "paragraph_funcs.h"
31 #include "ParagraphParameters.h"
32 #include "vspace.h"
33
34 #include "frontends/font_metrics.h"
35 #include "frontends/Painter.h"
36
37 #include "insets/insettext.h"
38
39 #include "support/textutils.h"
40
41 using lyx::pos_type;
42
43 using std::endl;
44 using std::max;
45 using std::string;
46
47 extern int NEST_MARGIN;
48 extern int CHANGEBAR_MARGIN;
49
50 namespace {
51
52 /**
53  * A class used for painting an individual row of text.
54  */
55 class RowPainter {
56 public:
57         /// initialise and run painter
58         RowPainter(BufferView const & bv, LyXText const & text,
59                 ParagraphList::iterator pit, RowList::iterator rit, int xo, int yo);
60 private:
61         // paint various parts
62         void paintBackground();
63         void paintSelection();
64         void paintAppendix();
65         void paintDepthBar();
66         void paintChangeBar();
67         void paintFirst();
68         void paintLast();
69         void paintForeignMark(double orig_x, LyXFont const & orig_font);
70         void paintHebrewComposeChar(lyx::pos_type & vpos);
71         void paintArabicComposeChar(lyx::pos_type & vpos);
72         void paintChars(lyx::pos_type & vpos, bool hebrew, bool arabic);
73         int paintAppendixStart(int y);
74         void paintText();
75         void paintFromPos(lyx::pos_type & vpos);
76         void paintInset(lyx::pos_type const pos);
77
78         /// return left margin
79         int leftMargin() const;
80
81         /// return the font at the given pos
82         LyXFont const getFont(lyx::pos_type pos) const;
83
84         /// return the label font for this row
85         LyXFont const getLabelFont() const;
86
87         /// return pixel width for the given pos
88         int singleWidth(lyx::pos_type pos) const;
89         int singleWidth(lyx::pos_type pos, char c) const;
90
91         /// bufferview to paint on
92         BufferView const & bv_;
93
94         /// Painter to use
95         Painter & pain_;
96
97         /// LyXText for the row
98         LyXText const & text_;
99
100         /// The row to paint
101         RowList::iterator const rit_;
102         Row & row_;
103
104         /// Row's paragraph
105         mutable ParagraphList::iterator  pit_;
106
107         // Looks ugly - is
108         double xo_;
109         int yo_;
110         double x_;
111         int width_;
112         double separator_;
113         double hfill_;
114         double label_hfill_;
115 };
116
117
118 RowPainter::RowPainter(BufferView const & bv, LyXText const & text,
119      ParagraphList::iterator pit, RowList::iterator rit,
120      int xo, int yo)
121         : bv_(bv), pain_(bv_.painter()), text_(text), rit_(rit), row_(*rit),
122           pit_(pit), xo_(xo), yo_(yo), x_(row_.x()),
123                 width_(text_.textWidth()),
124                 separator_(row_.fill_separator()),
125                 hfill_(row_.fill_hfill()),
126                 label_hfill_(row_.fill_label_hfill())
127 {
128         x_ += xo_;
129
130         // background has already been cleared.
131         if (&text_ == bv_.text())
132                 paintBackground();
133
134         // paint the selection background
135         if (bv_.cursor().selection() && &text_ == bv_.cursor().innerText())
136                 paintSelection();
137
138         // vertical lines for appendix
139         paintAppendix();
140
141         // environment depth brackets
142         paintDepthBar();
143
144         // changebar
145         paintChangeBar();
146
147         if (row_.pos() == 0)
148                 paintFirst();
149
150         if (row_.endpos() >= pit_->size())
151                 paintLast();
152
153         // paint text
154         paintText();
155 }
156
157
158 /// "temporary"
159 LyXFont const RowPainter::getFont(pos_type pos) const
160 {
161         return text_.getFont(pit_, pos);
162 }
163
164
165 int RowPainter::singleWidth(lyx::pos_type pos) const
166 {
167         return text_.singleWidth(pit_, pos);
168 }
169
170
171 int RowPainter::singleWidth(lyx::pos_type pos, char c) const
172 {
173         LyXFont const & font = text_.getFont(pit_, pos);
174         return text_.singleWidth(pit_, pos, c, font);
175 }
176
177
178 LyXFont const RowPainter::getLabelFont() const
179 {
180         return text_.getLabelFont(pit_);
181 }
182
183
184 int RowPainter::leftMargin() const
185 {
186         return text_.leftMargin(pit_, row_.pos());
187 }
188
189
190 void RowPainter::paintInset(pos_type const pos)
191 {
192         InsetBase * inset = const_cast<InsetBase *>(pit_->getInset(pos));
193         BOOST_ASSERT(inset);
194         PainterInfo pi(const_cast<BufferView *>(&bv_));
195         pi.base.font = getFont(pos);
196         inset->draw(pi, int(x_), yo_ + row_.baseline());
197         x_ += inset->width();
198 }
199
200
201 void RowPainter::paintHebrewComposeChar(pos_type & vpos)
202 {
203         pos_type pos = text_.bidi.vis2log(vpos);
204
205         string str;
206
207         // first char
208         char c = pit_->getChar(pos);
209         str += c;
210         ++vpos;
211
212         LyXFont const & font = getFont(pos);
213         int const width = font_metrics::width(c, font);
214         int dx = 0;
215
216         for (pos_type i = pos - 1; i >= 0; --i) {
217                 c = pit_->getChar(i);
218                 if (!Encodings::IsComposeChar_hebrew(c)) {
219                         if (IsPrintableNonspace(c)) {
220                                 int const width2 = singleWidth(i, c);
221                                 // dalet / resh
222                                 dx = (c == 'ø' || c == 'ã')
223                                         ? width2 - width
224                                         : (width2 - width) / 2;
225                         }
226                         break;
227                 }
228         }
229
230         // Draw nikud
231         pain_.text(int(x_) + dx, yo_ + row_.baseline(), str, font);
232 }
233
234
235 void RowPainter::paintArabicComposeChar(pos_type & vpos)
236 {
237         pos_type pos = text_.bidi.vis2log(vpos);
238         string str;
239
240         // first char
241         char c = pit_->getChar(pos);
242         c = pit_->transformChar(c, pos);
243         str += c;
244         ++vpos;
245
246         LyXFont const & font = getFont(pos);
247         int const width = font_metrics::width(c, font);
248         int dx = 0;
249
250         for (pos_type i = pos - 1; i >= 0; --i) {
251                 c = pit_->getChar(i);
252                 if (!Encodings::IsComposeChar_arabic(c)) {
253                         if (IsPrintableNonspace(c)) {
254                                 int const width2 = singleWidth(i, c);
255                                 dx = (width2 - width) / 2;
256                         }
257                         break;
258                 }
259         }
260         // Draw nikud
261         pain_.text(int(x_) + dx, yo_ + row_.baseline(), str, font);
262 }
263
264
265 void RowPainter::paintChars(pos_type & vpos, bool hebrew, bool arabic)
266 {
267         pos_type pos = text_.bidi.vis2log(vpos);
268         pos_type const end = row_.endpos();
269         LyXFont orig_font = getFont(pos);
270
271         // first character
272         string str;
273         str += pit_->getChar(pos);
274         if (arabic) {
275                 unsigned char c = str[0];
276                 str[0] = pit_->transformChar(c, pos);
277         }
278
279         bool prev_struckout = isDeletedText(*pit_, pos);
280         bool prev_newtext = isInsertedText(*pit_, pos);
281
282         // collect as much similar chars as we can
283         for (++vpos; vpos < end && (pos = text_.bidi.vis2log(vpos)) >= 0; ++vpos) {
284                 char c = pit_->getChar(pos);
285
286                 if (!IsPrintableNonspace(c))
287                         break;
288
289                 if (prev_struckout != isDeletedText(*pit_, pos))
290                         break;
291
292                 if (prev_newtext != isInsertedText(*pit_, pos))
293                         break;
294
295                 if (arabic && Encodings::IsComposeChar_arabic(c))
296                         break;
297
298                 if (hebrew && Encodings::IsComposeChar_hebrew(c))
299                         break;
300
301                 if (orig_font != getFont(pos))
302                         break;
303
304                 if (arabic)
305                         c = pit_->transformChar(c, pos);
306
307                 str += c;
308         }
309
310         if (prev_struckout)
311                 orig_font.setColor(LColor::strikeout);
312         else if (prev_newtext)
313                 orig_font.setColor(LColor::newtext);
314
315         // Draw text and set the new x position
316         //lyxerr << "paint row: yo_ " << yo_ << " baseline: " << row_.baseline()
317         //      << "\n";
318         pain_.text(int(x_), yo_ + row_.baseline(), str, orig_font);
319         x_ += font_metrics::width(str, orig_font);
320 }
321
322
323 void RowPainter::paintForeignMark(double orig_x, LyXFont const & orig_font)
324 {
325         if (!lyxrc.mark_foreign_language)
326                 return;
327         if (orig_font.language() == latex_language)
328                 return;
329         if (orig_font.language() == bv_.buffer()->params().language)
330                 return;
331
332         int const y = yo_ + row_.baseline() + 1;
333         pain_.line(int(orig_x), y, int(x_), y, LColor::language);
334 }
335
336
337 void RowPainter::paintFromPos(pos_type & vpos)
338 {
339         pos_type const pos = text_.bidi.vis2log(vpos);
340
341         LyXFont const & orig_font = getFont(pos);
342
343         double const orig_x = x_;
344
345         char const c = pit_->getChar(pos);
346
347         if (c == Paragraph::META_INSET) {
348                 paintInset(pos);
349                 ++vpos;
350                 paintForeignMark(orig_x, orig_font);
351                 return;
352         }
353
354         // usual characters, no insets
355
356         // special case languages
357         bool const hebrew = (orig_font.language()->lang() == "hebrew");
358         bool const arabic =
359                 orig_font.language()->lang() == "arabic" &&
360                 (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
361                 lyxrc.font_norm_type == LyXRC::ISO_10646_1);
362
363         // draw as many chars as we can
364         if ((!hebrew && !arabic)
365                 || (hebrew && !Encodings::IsComposeChar_hebrew(c))
366                 || (arabic && !Encodings::IsComposeChar_arabic(c))) {
367                 paintChars(vpos, hebrew, arabic);
368         } else if (hebrew) {
369                 paintHebrewComposeChar(vpos);
370         } else if (arabic) {
371                 paintArabicComposeChar(vpos);
372         }
373
374         paintForeignMark(orig_x, orig_font);
375 }
376
377
378 void RowPainter::paintBackground()
379 {
380         int const x = int(xo_);
381         int const y = yo_ < 0 ? 0 : yo_;
382         int const h = yo_ < 0 ? row_.height() + yo_ : row_.height();
383         pain_.fillRectangle(x, y, width_, h, text_.backgroundColor());
384 }
385
386
387 void RowPainter::paintSelection()
388 {
389         bool const is_rtl = pit_->isRightToLeftPar(bv_.buffer()->params());
390
391         // the current selection
392         LCursor const & cur = bv_.cursor();
393         int const startx = text_.cursorX(cur.selBegin());
394         int const endx = text_.cursorX(cur.selEnd());
395         int const starty = text_.cursorY(cur.selBegin());
396         int const endy = text_.cursorY(cur.selEnd());
397         ParagraphList::iterator startpit = text_.getPar(cur.selBegin());
398         ParagraphList::iterator endpit = text_.getPar(cur.selEnd());
399         RowList::iterator startrow = startpit->getRow(cur.selBegin().pos());
400         RowList::iterator endrow = endpit->getRow(cur.selEnd().pos());
401         int const h = row_.height();
402
403         int const row_y = pit_->y + row_.y_offset();
404
405         bool const sel_starts_here = startpit == pit_ && startrow == rit_;
406         bool const sel_ends_here   = endpit == pit_ && endrow == rit_;
407         bool const sel_on_one_row  = sel_starts_here && sel_ends_here;
408
409         if (text_.bidi.same_direction()) {
410                 int x;
411                 int w;
412                 if (sel_on_one_row) {
413                         if (startx < endx) {
414                                 x = int(xo_) + startx;
415                                 w = endx - startx;
416                         } else {
417                                 x = int(xo_) + endx;
418                                 w = startx - endx;
419                         }
420                         pain_.fillRectangle(x, yo_, w, h, LColor::selection);
421                 } else if (sel_starts_here) {
422                         int const x = is_rtl ? int(xo_) : int(xo_ + startx);
423                         int const w = is_rtl ? startx : (width_ - startx);
424                         pain_.fillRectangle(x, yo_, w, h, LColor::selection);
425                 } else if (sel_ends_here) {
426                         int const x = is_rtl ? int(xo_ + endx) : int(xo_);
427                         int const w = is_rtl ? (width_ - endx) : endx;
428                         pain_.fillRectangle(x, yo_, w, h, LColor::selection);
429                 } else if (row_y > starty && row_y < endy) {
430                         pain_.fillRectangle(int(xo_), yo_, width_, h, LColor::selection);
431                 }
432                 return;
433         }
434
435         if ((startpit != pit_ && startrow != rit_ && !is_rtl)
436                 || (endpit != pit_ && endrow != rit_ && is_rtl))
437                 pain_.fillRectangle(int(xo_), yo_,
438                         int(x_), h, LColor::selection);
439
440         pos_type const body_pos = pit_->beginOfBody();
441         pos_type const end = row_.endpos();
442         double tmpx = x_;
443
444         for (pos_type vpos = row_.pos(); vpos < end; ++vpos)  {
445                 pos_type pos = text_.bidi.vis2log(vpos);
446                 double const old_tmpx = tmpx;
447                 if (body_pos > 0 && pos == body_pos - 1) {
448                         LyXLayout_ptr const & layout = pit_->layout();
449                         LyXFont const lfont = getLabelFont();
450
451                         tmpx += label_hfill_ + font_metrics::width(layout->labelsep, lfont);
452
453                         if (pit_->isLineSeparator(body_pos - 1))
454                                 tmpx -= singleWidth(body_pos - 1);
455                 }
456
457                 tmpx += singleWidth(pos);
458
459                 if (hfillExpansion(*pit_, row_, pos)) {
460                         if (pos >= body_pos)
461                                 tmpx += hfill_;
462                         else
463                                 tmpx += label_hfill_;
464                 } else {
465                         if (pit_->isSeparator(pos) && pos >= body_pos)
466                                 tmpx += separator_;
467                 }
468
469                 if (((startpit != pit_ && startrow != rit_)
470                                 || cur.selBegin().pos() <= pos) &&
471                         ((endpit != pit_ && endrow != rit_)
472                                 || pos < cur.selEnd().pos())) {
473                         // Here we do not use x_ as xo_ was added to x_.
474                         pain_.fillRectangle(int(old_tmpx), yo_,
475                                 int(tmpx - old_tmpx + 1), h, LColor::selection);
476                 }
477         }
478
479         if ((startpit != pit_ && startrow != rit_ && is_rtl) ||
480             (endpit != pit_ && endrow != rit_ && !is_rtl)) {
481                 pain_.fillRectangle(int(xo_ + tmpx),
482                         yo_, int(width_ - tmpx), h, LColor::selection);
483         }
484 }
485
486
487 void RowPainter::paintChangeBar()
488 {
489         pos_type const start = row_.pos();
490         pos_type const end = row_.endpos();
491
492         if (start == end || !pit_->isChanged(start, end - 1))
493                 return;
494
495         int const height = text_.isLastRow(pit_, row_)
496                 ? row_.baseline()
497                 : row_.height() + boost::next(rit_)->top_of_text();
498
499         pain_.fillRectangle(4, yo_, 5, height, LColor::changebar);
500 }
501
502
503 void RowPainter::paintAppendix()
504 {
505         if (!pit_->params().appendix())
506                 return;
507
508         // FIXME: can be just width_ ?
509         int const ww = width_;
510
511         int y = yo_;
512
513         if (pit_->params().startOfAppendix())
514                 y += 2 * defaultRowHeight();
515
516         pain_.line(1, y, 1, yo_ + row_.height(), LColor::appendix);
517         pain_.line(ww - 2, y, ww - 2, yo_ + row_.height(), LColor::appendix);
518 }
519
520
521 void RowPainter::paintDepthBar()
522 {
523         Paragraph::depth_type const depth = pit_->getDepth();
524
525         if (depth <= 0)
526                 return;
527
528         Paragraph::depth_type prev_depth = 0;
529         if (!text_.isFirstRow(pit_, row_)) {
530                 ParagraphList::iterator pit2 = pit_;
531                 if (row_.pos() == 0)
532                         --pit2;
533                 prev_depth = pit2->getDepth();
534         }
535
536         Paragraph::depth_type next_depth = 0;
537         if (!text_.isLastRow(pit_, row_)) {
538                 ParagraphList::iterator pit2 = pit_;
539                 if (row_.endpos() >= pit2->size())
540                         ++pit2;
541                 next_depth = pit2->getDepth();
542         }
543
544         for (Paragraph::depth_type i = 1; i <= depth; ++i) {
545                 int const w = NEST_MARGIN / 5;
546                 int x = int(w * i + xo_);
547                 // only consider the changebar space if we're drawing outer left
548                 if (xo_ == 0)
549                         x += CHANGEBAR_MARGIN;
550                 int const h = yo_ + row_.height() - 1 - (i - next_depth - 1) * 3;
551
552                 pain_.line(x, yo_, x, h, LColor::depthbar);
553
554                 if (i > prev_depth)
555                         pain_.fillRectangle(x, yo_, w, 2, LColor::depthbar);
556                 if (i > next_depth)
557                         pain_.fillRectangle(x, h, w, 2, LColor::depthbar);
558         }
559 }
560
561
562 int RowPainter::paintAppendixStart(int y)
563 {
564         LyXFont pb_font;
565         pb_font.setColor(LColor::appendix);
566         pb_font.decSize();
567
568         string const label = _("Appendix");
569         int w = 0;
570         int a = 0;
571         int d = 0;
572         font_metrics::rectText(label, pb_font, w, a, d);
573
574         int const text_start = int(xo_ + (width_ - w) / 2);
575         int const text_end = text_start + w;
576
577         pain_.rectText(text_start, y + d, label, pb_font, LColor::none, LColor::none);
578
579         pain_.line(int(xo_ + 1), y, text_start, y, LColor::appendix);
580         pain_.line(text_end, y, int(xo_ + width_ - 2), y, LColor::appendix);
581
582         return 3 * defaultRowHeight();
583 }
584
585
586 void RowPainter::paintFirst()
587 {
588         ParagraphParameters const & parparams = pit_->params();
589
590         int y_top = 0;
591
592         // start of appendix?
593         if (parparams.startOfAppendix())
594                 y_top += paintAppendixStart(yo_ + y_top + 2 * defaultRowHeight());
595
596         Buffer const & buffer = *bv_.buffer();
597
598         LyXLayout_ptr const & layout = pit_->layout();
599
600         if (buffer.params().paragraph_separation == BufferParams::PARSEP_SKIP) {
601                 if (pit_ != text_.paragraphs().begin()) {
602                         if (layout->latextype == LATEX_PARAGRAPH
603                                 && !pit_->getDepth()) {
604                                 y_top += buffer.params().getDefSkip().inPixels(bv_);
605                         } else {
606                                 LyXLayout_ptr const & playout =
607                                         boost::prior(pit_)->layout();
608                                 if (playout->latextype == LATEX_PARAGRAPH
609                                         && !boost::prior(pit_)->getDepth()) {
610                                         // is it right to use defskip here, too? (AS)
611                                         y_top += buffer.params().getDefSkip().inPixels(bv_);
612                                 }
613                         }
614                 }
615         }
616
617         int const ww = bv_.workWidth();
618
619         bool const is_rtl = pit_->isRightToLeftPar(bv_.buffer()->params());
620         bool const is_seq = isFirstInSequence(pit_, text_.paragraphs());
621         //lyxerr << "paintFirst: " << pit_->id() << " is_seq: " << is_seq << std::endl;
622
623         // should we print a label?
624         if (layout->labeltype >= LABEL_STATIC
625             && (layout->labeltype != LABEL_STATIC
626                       || layout->latextype != LATEX_ENVIRONMENT
627                       || is_seq)) {
628
629                 LyXFont font = getLabelFont();
630                 if (!pit_->getLabelstring().empty()) {
631                         double x = x_;
632                         string const str = pit_->getLabelstring();
633
634                         // this is special code for the chapter layout. This is
635                         // printed in an extra row and has a pagebreak at
636                         // the top.
637                         if (layout->counter == "chapter") {
638                                 if (buffer.params().secnumdepth >= 0) {
639                                         float spacing_val = 1.0;
640                                         if (!parparams.spacing().isDefault()) {
641                                                 spacing_val = parparams.spacing().getValue();
642                                         } else {
643                                                 spacing_val = buffer.params().spacing().getValue();
644                                         }
645
646                                         int const maxdesc =
647                                                 int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val)
648                                                 + int(layout->parsep) * defaultRowHeight();
649
650                                         if (is_rtl) {
651                                                 x = ww - leftMargin() -
652                                                         font_metrics::width(str, font);
653                                         }
654
655                                         pain_.text(int(x),
656                                                 yo_ + row_.baseline() -
657                                                 row_.ascent_of_text() - maxdesc,
658                                                 str, font);
659                                 }
660                         } else {
661                                 if (is_rtl) {
662                                         x = ww - leftMargin()
663                                                 + font_metrics::width(layout->labelsep, font);
664                                 } else {
665                                         x = x_ - font_metrics::width(layout->labelsep, font)
666                                                 - font_metrics::width(str, font);
667                                 }
668
669                                 pain_.text(int(x), yo_ + row_.baseline(), str, font);
670                         }
671                 }
672
673         // the labels at the top of an environment.
674         // More or less for bibliography
675         } else if (is_seq &&
676                 (layout->labeltype == LABEL_TOP_ENVIRONMENT ||
677                 layout->labeltype == LABEL_BIBLIO ||
678                 layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)) {
679                 LyXFont font = getLabelFont();
680                 if (!pit_->getLabelstring().empty()) {
681                         string const str = pit_->getLabelstring();
682                         float spacing_val = 1.0;
683                         if (!parparams.spacing().isDefault()) {
684                                 spacing_val = parparams.spacing().getValue();
685                         } else {
686                                 spacing_val = buffer.params().spacing().getValue();
687                         }
688
689                         int maxdesc =
690                                 int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val
691                                 + (layout->labelbottomsep * defaultRowHeight()));
692
693                         double x = x_;
694                         if (layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
695                                 x = ((is_rtl ? leftMargin() : x_)
696                                          + ww - text_.rightMargin(*pit_)) / 2;
697                                 x -= font_metrics::width(str, font) / 2;
698                         } else if (is_rtl) {
699                                 x = ww - leftMargin() -
700                                         font_metrics::width(str, font);
701                         }
702                         pain_.text(int(x),
703                             yo_ + row_.baseline() - row_.ascent_of_text() - maxdesc,
704                                   str, font);
705                 }
706         }
707 }
708
709
710 void RowPainter::paintLast()
711 {
712         int const ww = bv_.workWidth();
713         bool const is_rtl = pit_->isRightToLeftPar(bv_.buffer()->params());
714         int const endlabel = getEndLabel(pit_, text_.paragraphs());
715
716         // draw an endlabel
717         switch (endlabel) {
718         case END_LABEL_BOX:
719         case END_LABEL_FILLED_BOX: {
720                 LyXFont const font = getLabelFont();
721                 int const size = int(0.75 * font_metrics::maxAscent(font));
722                 int const y = yo_ + row_.baseline() - size;
723                 int x = is_rtl ? NEST_MARGIN + CHANGEBAR_MARGIN: ww - size;
724
725                 if (row_.fill() <= size)
726                         x += (size - row_.fill() + 1) * (is_rtl ? -1 : 1);
727
728                 if (endlabel == END_LABEL_BOX)
729                         pain_.rectangle(x, y, size, size, LColor::eolmarker);
730                 else
731                         pain_.fillRectangle(x, y, size, size, LColor::eolmarker);
732                 break;
733         }
734
735         case END_LABEL_STATIC: {
736                 LyXFont font = getLabelFont();
737                 string const & str = pit_->layout()->endlabelstring();
738                 double const x = is_rtl ?
739                         x_ - font_metrics::width(str, font)
740                         : ww - text_.rightMargin(*pit_) - row_.fill();
741                 pain_.text(int(x), yo_ + row_.baseline(), str, font);
742                 break;
743         }
744
745         case END_LABEL_NO_LABEL:
746                 break;
747         }
748 }
749
750
751 void RowPainter::paintText()
752 {
753         pos_type const end = row_.endpos();
754         pos_type body_pos = pit_->beginOfBody();
755         if (body_pos > 0 &&
756                 (body_pos > end || !pit_->isLineSeparator(body_pos - 1))) {
757                 body_pos = 0;
758         }
759
760         LyXLayout_ptr const & layout = pit_->layout();
761
762         bool running_strikeout = false;
763         bool is_struckout = false;
764         int last_strikeout_x = 0;
765
766         for (pos_type vpos = row_.pos(); vpos < end; ) {
767                 if (x_ > bv_.workWidth())
768                         break;
769
770                 pos_type pos = text_.bidi.vis2log(vpos);
771
772                 if (pos >= pit_->size()) {
773                         ++vpos;
774                         continue;
775                 }
776
777                 if (x_ + singleWidth(pos) < 0) {
778                         x_ += singleWidth(pos);
779                         ++vpos;
780                         continue;
781                 }
782
783                 is_struckout = isDeletedText(*pit_, pos);
784
785                 if (is_struckout && !running_strikeout) {
786                         running_strikeout = true;
787                         last_strikeout_x = int(x_);
788                 }
789
790                 bool const highly_editable_inset = pit_->isInset(pos)
791                         && isHighlyEditableInset(pit_->getInset(pos));
792
793                 // if we reach the end of a struck out range, paint it
794                 // we also don't paint across things like tables
795                 if (running_strikeout && (highly_editable_inset || !is_struckout)) {
796                         int const middle = yo_ + (row_.baseline() + row_.top_of_text()) / 2;
797                         pain_.line(last_strikeout_x, middle, int(x_), middle,
798                                 LColor::strikeout, Painter::line_solid, Painter::line_thin);
799                         running_strikeout = false;
800                 }
801
802                 if (body_pos > 0 && pos == body_pos - 1) {
803                         int const lwidth = font_metrics::width(layout->labelsep,
804                                 getLabelFont());
805
806                         x_ += label_hfill_ + lwidth - singleWidth(body_pos - 1);
807                 }
808
809                 if (pit_->isHfill(pos)) {
810                         x_ += 1;
811
812                         int const y0 = yo_ + row_.baseline();
813                         int const y1 = y0 - defaultRowHeight() / 2;
814
815                         pain_.line(int(x_), y1, int(x_), y0, LColor::added_space);
816
817                         if (hfillExpansion(*pit_, row_, pos)) {
818                                 int const y2 = (y0 + y1) / 2;
819
820                                 if (pos >= body_pos) {
821                                         pain_.line(int(x_), y2, int(x_ + hfill_), y2,
822                                                   LColor::added_space,
823                                                   Painter::line_onoffdash);
824                                         x_ += hfill_;
825                                 } else {
826                                         pain_.line(int(x_), y2, int(x_ + label_hfill_), y2,
827                                                   LColor::added_space,
828                                                   Painter::line_onoffdash);
829                                         x_ += label_hfill_;
830                                 }
831                                 pain_.line(int(x_), y1, int(x_), y0, LColor::added_space);
832                         }
833                         x_ += 2;
834                         ++vpos;
835                 } else if (pit_->isSeparator(pos)) {
836                         x_ += singleWidth(pos);
837                         if (pos >= body_pos)
838                                 x_ += separator_;
839                         ++vpos;
840                 } else {
841                         paintFromPos(vpos);
842                 }
843         }
844
845         // if we reach the end of a struck out range, paint it
846         if (running_strikeout) {
847                 int const middle = yo_ + (row_.baseline() + row_.top_of_text()) / 2;
848                 pain_.line(last_strikeout_x, middle, int(x_), middle,
849                         LColor::strikeout, Painter::line_solid, Painter::line_thin);
850                 running_strikeout = false;
851         }
852 }
853
854
855 int paintPars(BufferView const & bv, LyXText const & text,
856         ParagraphList::iterator pit, int xo, int yo, int y)
857 {
858         //lyxerr << "  paintRows: pit: " << &*pit << endl;
859         int const y2 = bv.painter().paperHeight();
860         y -= bv.top_y();
861
862         ParagraphList::iterator end = text.paragraphs().end();
863
864         for ( ; pit != end; ++pit) {
865                 RowList::iterator row = pit->rows.begin();
866                 RowList::iterator rend = pit->rows.end();
867
868                 for ( ; row != rend; ++row) {
869                         RowPainter(bv, text, pit, row, xo, y + yo);
870                         y += row->height();
871                 }
872                 if (yo + y >= y2)
873                         break;
874         }
875
876         return y;
877 }
878
879 } // namespace anon
880
881
882 int paintText(BufferView const & bv)
883 {
884         ParagraphList::iterator pit;
885         bv.text()->getRowNearY(bv.top_y(), pit);
886         return paintPars(bv, *bv.text(), pit, 0, 0, pit->y);
887 }
888
889
890 void paintTextInset(LyXText const & text, PainterInfo & pi, int xo, int yo)
891 {
892         paintPars(*pi.base.bv, text, text.paragraphs().begin(), xo, yo, 0);
893 }