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