3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
15 #include "rowpainter.h"
19 #include "CoordCache.h"
21 #include "BufferParams.h"
22 #include "BufferView.h"
29 #include "MetricsInfo.h"
30 #include "Paragraph.h"
31 #include "ParagraphMetrics.h"
32 #include "paragraph_funcs.h"
33 #include "ParagraphParameters.h"
34 #include "TextMetrics.h"
37 #include "frontends/FontMetrics.h"
38 #include "frontends/Painter.h"
40 #include "insets/InsetText.h"
42 #include "support/debug.h"
43 #include "support/gettext.h"
44 #include "support/textutils.h"
46 #include "support/lassert.h"
47 #include <boost/crc.hpp>
53 using frontend::Painter
;
54 using frontend::FontMetrics
;
56 RowPainter::RowPainter(PainterInfo
& pi
,
57 Text
const & text
, pit_type pit
, Row
const & row
, Bidi
& bidi
, int x
, int y
)
58 : pi_(pi
), text_(text
),
59 text_metrics_(pi_
.base
.bv
->textMetrics(&text
)),
60 pars_(text
.paragraphs()),
61 row_(row
), pit_(pit
), par_(text
.paragraphs()[pit
]),
62 pm_(text_metrics_
.parMetrics(pit
)),
63 bidi_(bidi
), change_(pi_
.change_
),
64 xo_(x
), yo_(y
), width_(text_metrics_
.width())
66 bidi_
.computeTables(par_
, pi_
.base
.bv
->buffer(), row_
);
69 //lyxerr << "RowPainter: x: " << x_ << " xo: " << xo_ << " yo: " << yo_ << endl;
72 LASSERT(pit
>= 0, /**/);
73 LASSERT(pit
< int(text
.paragraphs().size()), /**/);
77 FontInfo
RowPainter::labelFont() const
79 return text_
.labelFont(pi_
.base
.bv
->buffer(), par_
);
83 int RowPainter::leftMargin() const
85 return text_metrics_
.leftMargin(text_metrics_
.width(), pit_
,
89 // If you want to debug inset metrics uncomment the following line:
90 //#define DEBUG_METRICS
91 // This draws green lines around each inset.
94 void RowPainter::paintInset(Inset
const * inset
, pos_type
const pos
)
96 Font
const font
= text_metrics_
.displayFont(pit_
, pos
);
98 LASSERT(inset
, return);
99 // Backup full_repaint status because some insets (InsetTabular)
100 // requires a full repaint
101 bool pi_full_repaint
= pi_
.full_repaint
;
103 // FIXME: We should always use font, see documentation of
104 // noFontChange() in Inset.h.
105 pi_
.base
.font
= inset
->noFontChange() ?
106 pi_
.base
.bv
->buffer().params().getFont().fontInfo() :
108 pi_
.ltr_pos
= (bidi_
.level(pos
) % 2 == 0);
109 pi_
.change_
= change_
.changed() ? change_
: par_
.lookupChange(pos
);
111 int const x1
= int(x_
);
112 pi_
.base
.bv
->coordCache().insets().add(inset
, x1
, yo_
);
113 // insets are painted completely. Recursive
114 inset
->drawBackground(pi_
, x1
, yo_
);
115 inset
->drawSelection(pi_
, x1
, yo_
);
116 inset
->draw(pi_
, x1
, yo_
);
118 Dimension
const & dim
= pm_
.insetDimension(inset
);
120 paintForeignMark(x_
, font
.language(), dim
.descent());
124 // Restore full_repaint status.
125 pi_
.full_repaint
= pi_full_repaint
;
128 int const x2
= x1
+ dim
.wid
;
129 int const y1
= yo_
+ dim
.des
;
130 int const y2
= yo_
- dim
.asc
;
131 pi_
.pain
.line(x1
, y1
, x1
, y2
, Color_green
);
132 pi_
.pain
.line(x1
, y1
, x2
, y1
, Color_green
);
133 pi_
.pain
.line(x2
, y1
, x2
, y2
, Color_green
);
134 pi_
.pain
.line(x1
, y2
, x2
, y2
, Color_green
);
139 void RowPainter::paintHebrewComposeChar(pos_type
& vpos
, FontInfo
const & font
)
141 pos_type pos
= bidi_
.vis2log(vpos
);
146 char_type c
= par_
.getChar(pos
);
150 int const width
= theFontMetrics(font
).width(c
);
153 for (pos_type i
= pos
- 1; i
>= 0; --i
) {
155 if (!Encodings::isHebrewComposeChar(c
)) {
156 if (isPrintableNonspace(c
)) {
157 int const width2
= pm_
.singleWidth(i
,
158 text_metrics_
.displayFont(pit_
, i
));
159 dx
= (c
== 0x05e8 || // resh
160 c
== 0x05d3) // dalet
162 : (width2
- width
) / 2;
169 pi_
.pain
.text(int(x_
) + dx
, yo_
, str
, font
);
173 void RowPainter::paintArabicComposeChar(pos_type
& vpos
, FontInfo
const & font
)
175 pos_type pos
= bidi_
.vis2log(vpos
);
179 char_type c
= par_
.getChar(pos
);
180 c
= par_
.transformChar(c
, pos
);
184 int const width
= theFontMetrics(font
).width(c
);
187 for (pos_type i
= pos
- 1; i
>= 0; --i
) {
189 if (!Encodings::isArabicComposeChar(c
)) {
190 if (isPrintableNonspace(c
)) {
191 int const width2
= pm_
.singleWidth(i
,
192 text_metrics_
.displayFont(pit_
, i
));
193 dx
= (width2
- width
) / 2;
199 pi_
.pain
.text(int(x_
) + dx
, yo_
, str
, font
);
203 void RowPainter::paintChars(pos_type
& vpos
, FontInfo
const & font
,
204 bool hebrew
, bool arabic
)
206 // This method takes up 70% of time when typing
207 pos_type pos
= bidi_
.vis2log(vpos
);
209 vector
<char_type
> str
;
211 str
.push_back(par_
.getChar(pos
));
214 char_type c
= str
[0];
219 str
[0] = par_
.transformChar(c
, pos
);
222 pos_type
const end
= row_
.endpos();
223 FontSpan
const font_span
= par_
.fontSpan(pos
);
224 // Track-change status.
225 Change
const & change_running
= par_
.lookupChange(pos
);
228 bool const selection
= pos
>= row_
.sel_beg
&& pos
< row_
.sel_end
;
230 char_type prev_char
= ' ';
231 // collect as much similar chars as we can
232 for (++vpos
; vpos
< end
; ++vpos
) {
233 pos
= bidi_
.vis2log(vpos
);
234 if (pos
< font_span
.first
|| pos
> font_span
.last
)
237 bool const new_selection
= pos
>= row_
.sel_beg
&& pos
< row_
.sel_end
;
238 if (new_selection
!= selection
)
239 // Selection ends or starts here.
242 Change
const & change
= par_
.lookupChange(pos
);
243 if (!change_running
.isSimilarTo(change
))
244 // Track change type or author has changed.
247 char_type c
= par_
.getChar(pos
);
249 if (c
== '\t' || prev_char
== '\t') {
254 if (!isPrintableNonspace(c
))
257 /* Because we do our own bidi, at this point the strings are
258 * already in visual order. However, Qt also applies its own
259 * bidi algorithm to strings that it paints to the screen.
260 * Therefore, if we were to paint Hebrew/Arabic words as a
261 * single string, the letters in the words would get reversed
262 * again. In order to avoid that, we don't collect Hebrew/
263 * Arabic characters, but rather paint them one at a time.
264 * See also http://thread.gmane.org/gmane.editors.lyx.devel/79740
269 /* FIXME: these checks are irrelevant, since 'arabic' and
270 * 'hebrew' alone are already going to trigger a break.
271 * However, this should not be removed completely, because
272 * if an alternative solution is found which allows grouping
273 * of arabic and hebrew characters, then these breaks may have
276 if (arabic && Encodings::isArabicComposeChar(c))
279 if (hebrew && Encodings::isHebrewComposeChar(c))
288 c
= par_
.transformChar(c
, pos
);
289 /* see comment in hebrew, explaining why we break */
296 docstring
s(&str
[0], str
.size());
299 s
.replace(0,1,from_ascii(" "));
301 if (!selection
&& !change_running
.changed()) {
302 x_
+= pi_
.pain
.text(int(x_
), yo_
, s
, font
);
306 FontInfo copy
= font
;
307 if (change_running
.changed())
308 copy
.setPaintColor(change_running
.color());
310 copy
.setPaintColor(Color_selectiontext
);
312 x_
+= pi_
.pain
.text(int(x_
), yo_
, s
, copy
);
316 void RowPainter::paintForeignMark(double orig_x
, Language
const * lang
,
319 if (!lyxrc
.mark_foreign_language
)
321 if (lang
== latex_language
)
323 if (lang
== pi_
.base
.bv
->buffer().params().language
)
326 int const y
= yo_
+ 1 + desc
;
327 pi_
.pain
.line(int(orig_x
), y
, int(x_
), y
, Color_language
);
331 void RowPainter::paintMisspelledMark(double orig_x
, int desc
)
333 int const y
= yo_
+ desc
;
334 pi_
.pain
.wavyHorizontalLine(int(orig_x
), y
, int(x_
) - int(orig_x
), Color_red
);
338 void RowPainter::paintFromPos(pos_type
& vpos
)
340 pos_type
const pos
= bidi_
.vis2log(vpos
);
341 Font
const orig_font
= text_metrics_
.displayFont(pit_
, pos
);
342 double const orig_x
= x_
;
344 // usual characters, no insets
345 char_type
const c
= par_
.getChar(pos
);
347 // special case languages
348 string
const & lang
= orig_font
.language()->lang();
349 bool const hebrew
= lang
== "hebrew";
350 bool const arabic
= lang
== "arabic_arabtex" || lang
== "arabic_arabi" ||
353 // draw as many chars as we can
354 if ((!hebrew
&& !arabic
)
355 || (hebrew
&& !Encodings::isHebrewComposeChar(c
))
356 || (arabic
&& !Encodings::isArabicComposeChar(c
))) {
357 paintChars(vpos
, orig_font
.fontInfo(), hebrew
, arabic
);
359 paintHebrewComposeChar(vpos
, orig_font
.fontInfo());
361 paintArabicComposeChar(vpos
, orig_font
.fontInfo());
364 paintForeignMark(orig_x
, orig_font
.language());
366 if (orig_font
.isMisspelled())
367 paintMisspelledMark(orig_x
, 3);
371 void RowPainter::paintChangeBar()
373 pos_type
const start
= row_
.pos();
374 pos_type end
= row_
.endpos();
376 if (par_
.size() == end
) {
377 // this is the last row of the paragraph;
378 // thus, we must also consider the imaginary end-of-par character
382 if (start
== end
|| !par_
.isChanged(start
, end
))
385 int const height
= text_metrics_
.isLastRow(pit_
, row_
)
389 pi_
.pain
.fillRectangle(5, yo_
- row_
.ascent(), 3, height
, Color_changebar
);
393 void RowPainter::paintAppendix()
395 // only draw the appendix frame once (for the main text)
396 if (!par_
.params().appendix() || !text_
.isMainText(pi_
.base
.bv
->buffer()))
399 int y
= yo_
- row_
.ascent();
401 if (par_
.params().startOfAppendix())
402 y
+= 2 * defaultRowHeight();
404 pi_
.pain
.line(1, y
, 1, yo_
+ row_
.height(), Color_appendix
);
405 pi_
.pain
.line(width_
- 2, y
, width_
- 2, yo_
+ row_
.height(), Color_appendix
);
409 void RowPainter::paintDepthBar()
411 depth_type
const depth
= par_
.getDepth();
416 depth_type prev_depth
= 0;
417 if (!text_metrics_
.isFirstRow(pit_
, row_
)) {
418 pit_type pit2
= pit_
;
421 prev_depth
= pars_
[pit2
].getDepth();
424 depth_type next_depth
= 0;
425 if (!text_metrics_
.isLastRow(pit_
, row_
)) {
426 pit_type pit2
= pit_
;
427 if (row_
.endpos() >= pars_
[pit2
].size())
429 next_depth
= pars_
[pit2
].getDepth();
432 for (depth_type i
= 1; i
<= depth
; ++i
) {
433 int const w
= nestMargin() / 5;
434 int x
= int(xo_
) + w
* i
;
435 // only consider the changebar space if we're drawing outermost text
436 if (text_
.isMainText(pi_
.base
.bv
->buffer()))
437 x
+= changebarMargin();
439 int const starty
= yo_
- row_
.ascent();
440 int const h
= row_
.height() - 1 - (i
- next_depth
- 1) * 3;
442 pi_
.pain
.line(x
, starty
, x
, starty
+ h
, Color_depthbar
);
445 pi_
.pain
.fillRectangle(x
, starty
, w
, 2, Color_depthbar
);
447 pi_
.pain
.fillRectangle(x
, starty
+ h
, w
, 2, Color_depthbar
);
452 int RowPainter::paintAppendixStart(int y
)
454 FontInfo pb_font
= sane_font
;
455 pb_font
.setColor(Color_appendix
);
462 docstring
const label
= _("Appendix");
463 theFontMetrics(pb_font
).rectText(label
, w
, a
, d
);
465 int const text_start
= int(xo_
+ (width_
- w
) / 2);
466 int const text_end
= text_start
+ w
;
468 pi_
.pain
.rectText(text_start
, y
+ d
, label
, pb_font
, Color_none
, Color_none
);
470 pi_
.pain
.line(int(xo_
+ 1), y
, text_start
, y
, Color_appendix
);
471 pi_
.pain
.line(text_end
, y
, int(xo_
+ width_
- 2), y
, Color_appendix
);
473 return 3 * defaultRowHeight();
477 void RowPainter::paintFirst()
479 ParagraphParameters
const & parparams
= par_
.params();
483 // start of appendix?
484 if (parparams
.startOfAppendix())
485 y_top
+= paintAppendixStart(yo_
- row_
.ascent() + 2 * defaultRowHeight());
487 Buffer
const & buffer
= pi_
.base
.bv
->buffer();
488 Layout
const & layout
= par_
.layout();
490 if (buffer
.params().paragraph_separation
== BufferParams::ParagraphSkipSeparation
) {
492 if (layout
.latextype
== LATEX_PARAGRAPH
493 && !par_
.getDepth()) {
494 y_top
+= buffer
.params().getDefSkip().inPixels(*pi_
.base
.bv
);
496 Layout
const & playout
= pars_
[pit_
- 1].layout();
497 if (playout
.latextype
== LATEX_PARAGRAPH
498 && !pars_
[pit_
- 1].getDepth()) {
499 // is it right to use defskip here, too? (AS)
500 y_top
+= buffer
.params().getDefSkip().inPixels(*pi_
.base
.bv
);
506 bool const is_rtl
= text_
.isRTL(buffer
, par_
);
507 bool const is_seq
= isFirstInSequence(pit_
, text_
.paragraphs());
508 //lyxerr << "paintFirst: " << par_.id() << " is_seq: " << is_seq << endl;
510 // should we print a label?
511 if (layout
.labeltype
>= LABEL_STATIC
512 && (layout
.labeltype
!= LABEL_STATIC
513 || layout
.latextype
!= LATEX_ENVIRONMENT
516 FontInfo
const font
= labelFont();
517 FontMetrics
const & fm
= theFontMetrics(font
);
519 docstring
const str
= par_
.labelString();
523 // this is special code for the chapter layout. This is
524 // printed in an extra row and has a pagebreak at
526 if (layout
.counter
== "chapter") {
527 double spacing_val
= 1.0;
528 if (!parparams
.spacing().isDefault()) {
529 spacing_val
= parparams
.spacing().getValue();
531 spacing_val
= buffer
.params().spacing().getValue();
534 int const labeladdon
= int(fm
.maxHeight() * layout
.spacing
.getValue() * spacing_val
);
536 int const maxdesc
= int(fm
.maxDescent() * layout
.spacing
.getValue() * spacing_val
)
537 + int(layout
.parsep
) * defaultRowHeight();
540 x
= width_
- leftMargin() -
544 pi_
.pain
.text(int(x
), yo_
- maxdesc
- labeladdon
, str
, font
);
547 x
= width_
- leftMargin()
548 + fm
.width(layout
.labelsep
);
550 x
= x_
- fm
.width(layout
.labelsep
)
554 pi_
.pain
.text(int(x
), yo_
, str
, font
);
558 // the labels at the top of an environment.
559 // More or less for bibliography
561 (layout
.labeltype
== LABEL_TOP_ENVIRONMENT
||
562 layout
.labeltype
== LABEL_BIBLIO
||
563 layout
.labeltype
== LABEL_CENTERED_TOP_ENVIRONMENT
)) {
564 FontInfo
const font
= labelFont();
565 docstring
const str
= par_
.labelString();
567 double spacing_val
= 1.0;
568 if (!parparams
.spacing().isDefault())
569 spacing_val
= parparams
.spacing().getValue();
571 spacing_val
= buffer
.params().spacing().getValue();
573 FontMetrics
const & fm
= theFontMetrics(font
);
575 int const labeladdon
= int(fm
.maxHeight()
576 * layout
.spacing
.getValue() * spacing_val
);
579 int(fm
.maxDescent() * layout
.spacing
.getValue() * spacing_val
580 + (layout
.labelbottomsep
* defaultRowHeight()));
583 if (layout
.labeltype
== LABEL_CENTERED_TOP_ENVIRONMENT
) {
586 x
+= (width_
- text_metrics_
.rightMargin(pm_
) - leftMargin()) / 2;
587 x
-= fm
.width(str
) / 2;
589 x
= width_
- leftMargin() - fm
.width(str
);
591 pi_
.pain
.text(int(x
), yo_
- maxdesc
- labeladdon
, str
, font
);
597 void RowPainter::paintLast()
599 bool const is_rtl
= text_
.isRTL(pi_
.base
.bv
->buffer(), par_
);
600 int const endlabel
= getEndLabel(pit_
, text_
.paragraphs());
602 // paint imaginary end-of-paragraph character
604 Change
const & change
= par_
.lookupChange(par_
.size());
605 if (change
.changed()) {
606 FontMetrics
const & fm
=
607 theFontMetrics(pi_
.base
.bv
->buffer().params().getFont());
608 int const length
= fm
.maxAscent() / 2;
609 Color col
= change
.color();
611 pi_
.pain
.line(int(x_
) + 1, yo_
+ 2, int(x_
) + 1, yo_
+ 2 - length
, col
,
612 Painter::line_solid
, Painter::line_thick
);
614 if (change
.deleted()) {
615 pi_
.pain
.line(int(x_
) + 1 - length
, yo_
+ 2, int(x_
) + 1 + length
,
616 yo_
+ 2, col
, Painter::line_solid
, Painter::line_thick
);
618 pi_
.pain
.line(int(x_
) + 1 - length
, yo_
+ 2, int(x_
) + 1,
619 yo_
+ 2, col
, Painter::line_solid
, Painter::line_thick
);
628 case END_LABEL_FILLED_BOX
: {
629 FontInfo
const font
= labelFont();
630 FontMetrics
const & fm
= theFontMetrics(font
);
631 int const size
= int(0.75 * fm
.maxAscent());
632 int const y
= yo_
- size
;
633 int const max_row_width
= width_
- size
- Inset::TEXT_TO_INSET_OFFSET
;
634 int x
= is_rtl
? nestMargin() + changebarMargin()
635 : max_row_width
- text_metrics_
.rightMargin(pm_
);
637 // If needed, move the box a bit to avoid overlapping with text.
638 int const rem
= max_row_width
- row_
.width();
640 x
+= is_rtl
? rem
: - rem
;
642 if (endlabel
== END_LABEL_BOX
)
643 pi_
.pain
.rectangle(x
, y
, size
, size
, Color_eolmarker
);
645 pi_
.pain
.fillRectangle(x
, y
, size
, size
, Color_eolmarker
);
649 case END_LABEL_STATIC
: {
650 FontInfo
const font
= labelFont();
651 FontMetrics
const & fm
= theFontMetrics(font
);
652 docstring
const & str
= par_
.layout().endlabelstring();
653 double const x
= is_rtl
?
655 : - text_metrics_
.rightMargin(pm_
) - row_
.width();
656 pi_
.pain
.text(int(x
), yo_
, str
, font
);
660 case END_LABEL_NO_LABEL
:
666 void RowPainter::paintOnlyInsets()
668 CoordCache
const & cache
= pi_
.base
.bv
->coordCache();
669 pos_type
const end
= row_
.endpos();
670 for (pos_type pos
= row_
.pos(); pos
!= end
; ++pos
) {
671 // If outer row has changed, nested insets are repaint completely.
672 Inset
const * inset
= par_
.getInset(pos
);
675 if (x_
> pi_
.base
.bv
->workWidth()
676 || !cache
.getInsets().has(inset
))
678 x_
= cache
.getInsets().x(inset
);
680 bool const pi_selected
= pi_
.selected
;
681 Cursor
const & cur
= pi_
.base
.bv
->cursor();
682 if (cur
.selection() && cur
.text() == &text_
683 && cur
.anchor().text() == &text_
)
684 pi_
.selected
= row_
.sel_beg
<= pos
&& row_
.sel_end
> pos
;
685 paintInset(inset
, pos
);
686 pi_
.selected
= pi_selected
;
691 void RowPainter::paintText()
693 pos_type
const end
= row_
.endpos();
694 // Spaces at logical line breaks in bidi text must be skipped during
695 // painting. However, they may appear visually in the middle
696 // of a row; they must be skipped, wherever they are...
697 // * logically "abc_[HEBREW_\nHEBREW]"
698 // * visually "abc_[_WERBEH\nWERBEH]"
699 pos_type skipped_sep_vpos
= -1;
700 pos_type body_pos
= par_
.beginOfBody();
702 (body_pos
> end
|| !par_
.isLineSeparator(body_pos
- 1))) {
706 Layout
const & layout
= par_
.layout();
708 Change change_running
;
709 int change_last_x
= 0;
711 // check for possible inline completion
712 DocIterator
const & inlineCompletionPos
= pi_
.base
.bv
->inlineCompletionPos();
713 pos_type inlineCompletionVPos
= -1;
714 if (inlineCompletionPos
.inTexted()
715 && inlineCompletionPos
.text() == &text_
716 && inlineCompletionPos
.pit() == pit_
717 && inlineCompletionPos
.pos() - 1 >= row_
.pos()
718 && inlineCompletionPos
.pos() - 1 < row_
.endpos()) {
719 // draw logically behind the previous character
720 inlineCompletionVPos
= bidi_
.log2vis(inlineCompletionPos
.pos() - 1);
723 // Use font span to speed things up, see below
727 // If the last logical character is a separator, don't paint it, unless
728 // it's in the last row of a paragraph; see skipped_sep_vpos declaration
729 if (end
> 0 && end
< par_
.size() && par_
.isSeparator(end
- 1))
730 skipped_sep_vpos
= bidi_
.log2vis(end
- 1);
732 for (pos_type vpos
= row_
.pos(); vpos
< end
; ) {
733 if (x_
> pi_
.base
.bv
->workWidth())
736 // Skip the separator at the logical end of the row
737 if (vpos
== skipped_sep_vpos
) {
742 pos_type
const pos
= bidi_
.vis2log(vpos
);
744 if (pos
>= par_
.size()) {
749 // Use font span to speed things up, see above
750 if (vpos
< font_span
.first
|| vpos
> font_span
.last
) {
751 font_span
= par_
.fontSpan(vpos
);
752 font
= text_metrics_
.displayFont(pit_
, vpos
);
754 // split font span if inline completion is inside
755 if (font_span
.first
<= inlineCompletionVPos
756 && font_span
.last
> inlineCompletionVPos
)
757 font_span
.last
= inlineCompletionVPos
;
760 const int width_pos
= pm_
.singleWidth(pos
, font
);
762 if (x_
+ width_pos
< 0) {
767 Change
const & change
= par_
.lookupChange(pos
);
768 if (change
.changed() && !change_running
.changed()) {
769 change_running
= change
;
770 change_last_x
= int(x_
);
773 Inset
const * inset
= par_
.getInset(pos
);
774 bool const highly_editable_inset
= inset
775 && inset
->editable();
777 // If we reach the end of a change or if the author changes, paint it.
778 // We also don't paint across things like tables
779 if (change_running
.changed() && (highly_editable_inset
780 || !change
.changed() || !change_running
.isSimilarTo(change
))) {
781 // Calculate 1/3 height of the buffer's default font
782 FontMetrics
const & fm
783 = theFontMetrics(pi_
.base
.bv
->buffer().params().getFont());
784 int const y_bar
= change_running
.deleted() ?
785 yo_
- fm
.maxAscent() / 3 : yo_
+ fm
.maxAscent() / 6;
786 pi_
.pain
.line(change_last_x
, y_bar
, int(x_
), y_bar
,
787 change_running
.color(), Painter::line_solid
,
790 // Change might continue with a different author or type
791 if (change
.changed() && !highly_editable_inset
) {
792 change_running
= change
;
793 change_last_x
= int(x_
);
795 change_running
.setUnchanged();
798 if (body_pos
> 0 && pos
== body_pos
- 1) {
799 int const lwidth
= theFontMetrics(labelFont())
800 .width(layout
.labelsep
);
802 x_
+= row_
.label_hfill
+ lwidth
- width_pos
;
805 // Is the inline completion in front of character?
806 if (font
.isRightToLeft() && vpos
== inlineCompletionVPos
)
807 paintInlineCompletion(font
);
809 if (par_
.isSeparator(pos
)) {
810 Font
const orig_font
= text_metrics_
.displayFont(pit_
, pos
);
811 double const orig_x
= x_
;
814 x_
+= row_
.separator
;
815 paintForeignMark(orig_x
, orig_font
.language());
819 // If outer row has changed, nested insets are repaint completely.
820 pi_
.base
.bv
->coordCache().insets().add(inset
, int(x_
), yo_
);
822 bool const pi_selected
= pi_
.selected
;
823 Cursor
const & cur
= pi_
.base
.bv
->cursor();
824 if (cur
.selection() && cur
.text() == &text_
825 && cur
.anchor().text() == &text_
)
826 pi_
.selected
= row_
.sel_beg
<= pos
&& row_
.sel_end
> pos
;
827 paintInset(inset
, pos
);
828 pi_
.selected
= pi_selected
;
832 // paint as many characters as possible.
836 // Is the inline completion after character?
837 if (!font
.isRightToLeft() && vpos
- 1 == inlineCompletionVPos
)
838 paintInlineCompletion(font
);
841 // if we reach the end of a struck out range, paint it
842 if (change_running
.changed()) {
843 FontMetrics
const & fm
844 = theFontMetrics(pi_
.base
.bv
->buffer().params().getFont());
845 int const y_bar
= change_running
.deleted() ?
846 yo_
- fm
.maxAscent() / 3 : yo_
+ fm
.maxAscent() / 6;
847 pi_
.pain
.line(change_last_x
, y_bar
, int(x_
), y_bar
,
848 change_running
.color(), Painter::line_solid
, Painter::line_thin
);
849 change_running
.setUnchanged();
854 void RowPainter::paintInlineCompletion(Font
const & font
)
856 docstring completion
= pi_
.base
.bv
->inlineCompletion();
857 FontInfo f
= font
.fontInfo();
858 bool rtl
= font
.isRightToLeft();
860 // draw the unique and the non-unique completion part
861 // Note: this is not time-critical as it is
862 // only done once per screen.
863 size_t uniqueTo
= pi_
.base
.bv
->inlineCompletionUniqueChars();
864 docstring s1
= completion
.substr(0, uniqueTo
);
865 docstring s2
= completion
.substr(uniqueTo
);
866 ColorCode c1
= Color_inlinecompletion
;
867 ColorCode c2
= Color_nonunique_inlinecompletion
;
877 pi_
.pain
.text(int(x_
), yo_
, s1
, f
);
878 x_
+= theFontMetrics(font
).width(s1
);
883 pi_
.pain
.text(int(x_
), yo_
, s2
, f
);
884 x_
+= theFontMetrics(font
).width(s2
);