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