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