From 3461169526262fb05dcc7ec80bd38e2cc6511124 Mon Sep 17 00:00:00 2001 From: Abdelrazak Younes Date: Sun, 14 Sep 2008 14:32:40 +0000 Subject: [PATCH] Patch by Vincent that solves a number of problems related to the painting of a selection: 1. When a listing is inserted in a bit of text, the line above the listing is not drawn over the full width like it is done for lines above other insets. This is because InsetListing has a AlignLeft alignment. Now, if you start selecting downwards with the mouse in this empty area, strange selection drawings appear (see attachment). This is caused by the fact that starting your selection at such a place, causes beg.boundary() to be true in TextMetrics::drawRowSelection(..). This is correct, but this value is true for _all_ selected lines. Now, the selection acts as if it is RTL text. Therefore, just like for end.boundary, this value needs to be reset for every line. 2. Starting your selection in an end margin often causes the selection in this end margin to be painted later. This is because when starting your selection in an end margin, you may have set a (possible empty) selection before really selecting the end margin. The problem is that the checksum (computed later) is the same for this empty selection and for the end margin selection. Therfore, we need a call to cur.setSelection() before evaluating cur.selection(). 3. In the following two lines, it is assumed that there is only an end margin to be painted if the selection extends to the next paragraph. This is not true for the above described case of an AlignLeft Inset. Then, the margin has also be drawn within a paragraph 4. The end and begin margins are only painted when the selection extends into the following or previous paragraph. This difference is not resembled in the checksum if you first select a row completely and then procede to the next or previous paragraph as the selection remains at the end of a row. This also holds for the AlignLeft case. Therefore I added a term to the checksum to monitor whether the end and begin margins need to be drawn. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@26399 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/ParagraphMetrics.cpp | 7 ++-- src/Row.cpp | 54 ++++++++++++++++++++++++++-- src/Row.h | 24 ++++++++++++- src/TextMetrics.cpp | 78 ++++++++++++++++++++++------------------ src/TextMetrics.h | 3 +- 5 files changed, 123 insertions(+), 43 deletions(-) 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: -- 2.39.5