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