diff --git a/src/ParagraphMetrics.cpp b/src/ParagraphMetrics.cpp index 801642e525..1e4410644c 100644 --- a/src/ParagraphMetrics.cpp +++ b/src/ParagraphMetrics.cpp @@ -103,9 +103,10 @@ size_t ParagraphMetrics::computeRowSignature(Row const & row, } Dimension const & d = row.dimension(); - char_type const b[] = { row.sel_beg, row.sel_end, d.wid, d.asc, d.des}; - // Each of the variable to process is 4 bytes: 4x5 = 20 - crc.process_bytes(b, 20); + char_type const b[] = { row.sel_beg, row.sel_end, + row.left_margin_sel, row.right_margin_sel, d.wid, d.asc, d.des}; + // Each of the variable to process is 4 bytes: 4x7 = 28 + crc.process_bytes(b, 28); return crc.checksum(); } diff --git a/src/Row.cpp b/src/Row.cpp index 975940310c..8c8383b24f 100644 --- a/src/Row.cpp +++ b/src/Row.cpp @@ -18,6 +18,8 @@ #include "Row.h" +#include "DocIterator.h" + #include "support/debug.h" @@ -26,13 +28,15 @@ namespace lyx { Row::Row() : separator(0), label_hfill(0), x(0), - sel_beg(-1), sel_end(-1), changed_(false), crc_(0), pos_(0), end_(0) + sel_beg(-1), sel_end(-1), changed_(false), crc_(0), + pos_(0), end_(0), left_margin_sel(false), right_margin_sel(false) {} Row::Row(pos_type pos) : separator(0), label_hfill(0), x(0), - sel_beg(-1), sel_end(-1), changed_(false), crc_(0), pos_(pos), end_(0) + sel_beg(-1), sel_end(-1), changed_(false), crc_(0), + pos_(0), end_(0), left_margin_sel(false), right_margin_sel(false) {} @@ -61,6 +65,46 @@ void Row::endpos(pos_type p) } +bool Row::isMarginSelected(bool margin_begin, DocIterator const & beg, + DocIterator const & end) const +{ + pos_type const sel_pos = margin_begin ? sel_beg : sel_end; + pos_type const margin_pos = margin_begin ? pos_ : end_; + + // Is the chosen margin selected ? + if (sel_pos == margin_pos) { + if (beg.pos() == end.pos()) + // This is a special case in which the space between after + // pos i-1 and before pos i is selected, i.e. the margins + // (see DocIterator::boundary_). + return beg.boundary() && !end.boundary(); + else if (end.pos() == margin_pos) + // If the selection ends around the margin, it is only + // drawn if the cursor is after the margin. + return !end.boundary(); + else if (beg.pos() == margin_pos) + // If the selection begins around the margin, it is + // only drawn if the cursor is before the margin. + return beg.boundary(); + else + return true; + } + return false; +} + + +void Row::setSelectionAndMargins(DocIterator const & beg, + DocIterator const & end) const +{ + setSelection(beg.pos(), end.pos()); + + if (selection()) { + right_margin_sel = isMarginSelected(false, beg, end); + left_margin_sel = isMarginSelected(true, beg, end); + } +} + + void Row::setSelection(pos_type beg, pos_type end) const { if (pos_ >= beg && pos_ <= end) @@ -79,6 +123,12 @@ void Row::setSelection(pos_type beg, pos_type end) const } +bool Row::selection() const +{ + return sel_beg != -1 && sel_end != -1; +} + + void Row::dump(char const * s) const { LYXERR0(s << " pos: " << pos_ << " end: " << end_ diff --git a/src/Row.h b/src/Row.h index a3afad4c11..055608bc2e 100644 --- a/src/Row.h +++ b/src/Row.h @@ -22,6 +22,8 @@ namespace lyx { +class DocIterator; + /** * An on-screen row of text. A paragraph is broken into a * RowList for display. Each Row contains position pointers @@ -45,7 +47,13 @@ public: * time. */ void setSelection(pos_type sel_beg, pos_type sel_end) const; - + /// + bool selection() const; + /// Set the selection begin and end and whether the margin begin and end + /// are selected. + void setSelectionAndMargins(DocIterator const & beg, + DocIterator const & end) const; + /// void pos(pos_type p); /// @@ -80,7 +88,21 @@ public: mutable pos_type sel_beg; /// mutable pos_type sel_end; + /// + mutable bool left_margin_sel; + /// + mutable bool right_margin_sel; + private: + /// Decides whether the margin is selected. + /** + * \param margin_begin + * \param beg + * \param end + */ + bool isMarginSelected(bool margin_begin, DocIterator const & beg, + DocIterator const & end) const; + /// has the Row appearance changed since last drawing? mutable bool changed_; /// CRC of row contents. diff --git a/src/TextMetrics.cpp b/src/TextMetrics.cpp index 84d268afb4..cc25afb755 100644 --- a/src/TextMetrics.cpp +++ b/src/TextMetrics.cpp @@ -1986,15 +1986,19 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co && cur.anchor().text() == text_ && pit >= sel_beg.pit() && pit <= sel_end.pit(); + // We store the begin and end pos of the selection relative to this par + DocIterator sel_beg_par = cur.selectionBegin(); + DocIterator sel_end_par = cur.selectionEnd(); + // We care only about visible selection. if (selection) { if (pit != sel_beg.pit()) { - sel_beg.pit() = pit; - sel_beg.pos() = 0; + sel_beg_par.pit() = pit; + sel_beg_par.pos() = 0; } if (pit != sel_end.pit()) { - sel_end.pit() = pit; - sel_end.pos() = sel_end.lastpos(); + sel_end_par.pit() = pit; + sel_end_par.pos() = sel_end_par.lastpos(); } } @@ -2011,9 +2015,18 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co RowPainter rp(pi, *text_, pit, row, bidi, x, y); if (selection) - row.setSelection(sel_beg.pos(), sel_end.pos()); + row.setSelectionAndMargins(sel_beg_par, sel_end_par); else row.setSelection(-1, -1); + + // The row knows nothing about the paragraph, so we have to check + // whether this row is the first or last and update the margins. + if (row.selection()) { + if (row.sel_beg == 0) + row.left_margin_sel = sel_beg.pit() < pit; + if (row.sel_end == sel_end_par.lastpos()) + row.right_margin_sel = sel_end.pit() > pit; + } // Row signature; has row changed since last paint? row.setCrc(pm.computeRowSignature(row, bparams)); @@ -2035,34 +2048,18 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co pi.pain.fillRectangle(x, y - row.ascent(), width(), row.height(), pi.background_color); } - - bool row_selection = row.sel_beg != -1 && row.sel_end != -1; - if (row_selection) { - DocIterator beg = bv_->cursor().selectionBegin(); - DocIterator end = bv_->cursor().selectionEnd(); - // FIXME (not here): pit is not updated when extending - // a selection to a new row with cursor right/left - bool const beg_margin = beg.pit() < pit; - bool const end_margin = end.pit() > pit; - beg.pit() = pit; - beg.pos() = row.sel_beg; - end.pit() = pit; - end.pos() = row.sel_end; - if (end.pos() == row.endpos()) { - // selection goes till the end of the row. - end.boundary(true); - } - drawRowSelection(pi, x, row, beg, end, beg_margin, end_margin); - } + + if (row.selection()) + drawRowSelection(pi, x, row, cur, pit); // Instrumentation for testing row cache (see also // 12 lines lower): if (lyxerr.debugging(Debug::PAINTING) && inside - && (row_selection || pi.full_repaint || row_has_changed)) { + && (row.selection() || pi.full_repaint || row_has_changed)) { string const foreword = text_->isMainText(bv_->buffer()) ? "main text redraw " : "inset text redraw: "; LYXERR(Debug::PAINTING, foreword << "pit=" << pit << " row=" << i - << " row_selection=" << row_selection + << " row_selection=" << row.selection() << " full_repaint=" << pi.full_repaint << " row_has_changed=" << row_has_changed); } @@ -2091,28 +2088,39 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type pit, int x, int y) co void TextMetrics::drawRowSelection(PainterInfo & pi, int x, Row const & row, - DocIterator const & beg, DocIterator const & end, - bool drawOnBegMargin, bool drawOnEndMargin) const + Cursor const & curs, pit_type pit) const { + DocIterator beg = curs.selectionBegin(); + beg.pit() = pit; + beg.pos() = row.sel_beg; + + DocIterator end = curs.selectionEnd(); + end.pit() = pit; + end.pos() = row.sel_end; + + bool const begin_boundary = beg.pos() >= row.endpos(); + bool const end_boundary = row.sel_end == row.endpos(); + Buffer & buffer = bv_->buffer(); DocIterator cur = beg; - int x1 = cursorX(beg.top(), beg.boundary()); - int x2 = cursorX(end.top(), end.boundary()); + cur.boundary(begin_boundary); + int x1 = cursorX(beg.top(), begin_boundary); + int x2 = cursorX(end.top(), end_boundary); int y1 = bv_->getPos(cur, cur.boundary()).y_ - row.ascent(); int y2 = y1 + row.height(); // draw the margins - if (drawOnBegMargin) { + if (row.left_margin_sel) { if (text_->isRTL(buffer, beg.paragraph())) { - int lm = bv_->leftMargin(); - pi.pain.fillRectangle(x + x1, y1, width() - lm - x1, y2 - y1, Color_selection); + int const w = width() - bv_->leftMargin() - x1; + pi.pain.fillRectangle(x + x1, y1, w, y2 - y1, Color_selection); } else { - int rm = bv_->rightMargin(); + int const rm = bv_->rightMargin(); pi.pain.fillRectangle(rm, y1, x1 - rm, y2 - y1, Color_selection); } } - if (drawOnEndMargin) { + if (row.right_margin_sel) { if (text_->isRTL(buffer, beg.paragraph())) { int rm = bv_->rightMargin(); pi.pain.fillRectangle(x + rm, y1, x2 - rm, y2 - y1, Color_selection); diff --git a/src/TextMetrics.h b/src/TextMetrics.h index 1a0945d4ff..78825c3985 100644 --- a/src/TextMetrics.h +++ b/src/TextMetrics.h @@ -160,8 +160,7 @@ private: /// draw selection for a single row void drawRowSelection(PainterInfo & pi, int x, Row const & row, - DocIterator const & beg, DocIterator const & end, - bool drawOnBegMargin, bool drawOnEndMargin) const; + Cursor const & cur, pit_type const pit) const; // Temporary public: public: