X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FCursor.cpp;h=fe03a31095b83b840d79c3560e768ebbdf0ea84b;hb=f110ef2f41d0aca14acd4a5f1aec6011f770c3c6;hp=22b75b5a2613fd67c940db5a42af6f5edbc91fb6;hpb=4fcbb93e39800f6188a0bb70183f18fc7438ce17;p=lyx.git diff --git a/src/Cursor.cpp b/src/Cursor.cpp index 22b75b5a26..fe03a31095 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -36,9 +36,11 @@ #include "TextMetrics.h" #include "TocBackend.h" -#include "support/lassert.h" #include "support/debug.h" #include "support/docstream.h" +#include "support/ExceptionMessage.h" +#include "support/gettext.h" +#include "support/lassert.h" #include "insets/InsetTabular.h" #include "insets/InsetText.h" @@ -63,22 +65,6 @@ namespace lyx { namespace { -bool positionable(DocIterator const & cursor, DocIterator const & anchor) -{ - // avoid deeper nested insets when selecting - if (cursor.depth() > anchor.depth()) - return false; - - // anchor might be deeper, should have same path then - for (size_t i = 0; i < cursor.depth(); ++i) - if (&cursor[i].inset() != &anchor[i].inset()) - return false; - - // position should be ok. - return true; -} - - // Find position closest to (x, y) in cell given by iter. // Used only in mathed DocIterator bruteFind2(Cursor const & c, int x, int y) @@ -185,7 +171,7 @@ bool bruteFind(Cursor & cursor, } */ - +/* /// moves position closest to (x, y) in given box bool bruteFind3(Cursor & cur, int x, int y, bool up) { @@ -214,9 +200,10 @@ bool bruteFind3(Cursor & cur, int x, int y, bool up) for ( ; it != et; it.forwardPos()) { // avoid invalid nesting when selecting if (bv.cursorStatus(it) == CUR_INSIDE - && (!cur.selection() || positionable(it, cur.realAnchor()))) { - // If this function is ever used again, check whether this - // is the same as "bv.getPos(it, false)" with boundary = false. + && (!cur.selection() || positionable(it, cur.realAnchor()))) { + // If this function is ever used again, check + // whether this is the same as "bv.getPos(it, + // false)" with boundary = false. Point p = bv.getPos(it); int xo = p.x_; int yo = p.y_; @@ -246,17 +233,36 @@ bool bruteFind3(Cursor & cur, int x, int y, bool up) cur.setCursor(best_cursor); return true; } - +*/ } // namespace anon +CursorData::CursorData() + : DocIterator(), anchor_(), + selection_(false), mark_(false), word_selection_(false), + logicalpos_(false), current_font(inherit_font) +{} + + +CursorData::CursorData(Buffer * buffer) + : DocIterator(buffer), anchor_(), + selection_(false), mark_(false), word_selection_(false), + logicalpos_(false), current_font(inherit_font) +{} + + +CursorData::CursorData(DocIterator const & dit) + : DocIterator(dit), anchor_(), + selection_(false), mark_(false), word_selection_(false), + logicalpos_(false), current_font(inherit_font) +{} + + // be careful: this is called from the bv's constructor, too, so // bv functions are not yet available! Cursor::Cursor(BufferView & bv) - : DocIterator(&bv.buffer()), bv_(&bv), anchor_(), - x_target_(-1), textTargetOffset_(0), - selection_(false), mark_(false), word_selection_(false), - logicalpos_(false), current_font(inherit_font) + : CursorData(&bv.buffer()), bv_(&bv), + x_target_(-1), textTargetOffset_(0) {} @@ -266,6 +272,8 @@ void Cursor::reset() push_back(CursorSlice(buffer()->inset())); anchor_ = doc_iterator_begin(buffer()); anchor_.clear(); + new_word_ = doc_iterator_begin(buffer()); + new_word_.clear(); clearTargetX(); selection_ = false; mark_ = false; @@ -279,6 +287,25 @@ void Cursor::setCursor(DocIterator const & cur) } +void Cursor::setCursorToAnchor() +{ + if (selection()) { + DocIterator normal = anchor_; + while (depth() < normal.depth()) + normal.pop_back(); + if (depth() < anchor_.depth() && top() <= anchor_[depth() - 1]) + ++normal.pos(); + setCursor(normal); + } +} + + +void Cursor::setCursorData(CursorData const & data) +{ + CursorData::operator=(data); +} + + bool Cursor::getStatus(FuncRequest const & cmd, FuncStatus & status) const { Cursor cur = *this; @@ -299,6 +326,7 @@ bool Cursor::getStatus(FuncRequest const & cmd, FuncStatus & status) const bool res = false; for ( ; cur.depth(); cur.pop()) { //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl; + // LASSERT: Is it safe to continue here, or should we return? LASSERT(cur.idx() <= cur.lastidx(), /**/); LASSERT(cur.pit() <= cur.lastpit(), /**/); LASSERT(cur.pos() <= cur.lastpos(), /**/); @@ -334,7 +362,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) Cursor old = *this; disp_ = DispatchResult(); - buffer()->undo().beginUndoGroup(); + beginUndoGroup(); // Is this a function that acts on inset at point? if (lyxaction.funcHasFlag(cmd.action(), LyXAction::AtPoint) @@ -346,7 +374,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) << cmd0 << endl << *this); nextInset()->dispatch(*this, tmpcmd); if (disp_.dispatched()) { - buffer()->undo().endUndoGroup(); + endUndoGroup(); return; } } @@ -356,9 +384,12 @@ void Cursor::dispatch(FuncRequest const & cmd0) for (; depth(); pop(), boundary(false)) { LYXERR(Debug::DEBUG, "Cursor::dispatch: cmd: " << cmd0 << endl << *this); - LASSERT(pos() <= lastpos(), /**/); - LASSERT(idx() <= lastidx(), /**/); - LASSERT(pit() <= lastpit(), /**/); + + // In any of these cases, the cursor is invalid, and we should + // try to save this document rather than crash. + LBUFERR(pos() <= lastpos()); + LBUFERR(idx() <= lastidx()); + LBUFERR(pit() <= lastpit()); // The common case is 'LFUN handled, need update', so make the // LFUN handler's life easier by assuming this as default value. @@ -369,7 +400,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) if (disp_.dispatched()) break; } - + // it completely to get a 'bomb early' behaviour in case this // object will be used again. if (!disp_.dispatched()) { @@ -377,7 +408,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) // We might have invalidated the cursor when removing an empty // paragraph while the cursor could not be moved out the inset // while we initially thought we could. This might happen when - // a multiline inset becomes an inline inset when the second + // a multiline inset becomes an inline inset when the second // paragraph is removed. if (safe.pit() > safe.lastpit()) { safe.pit() = safe.lastpit(); @@ -391,7 +422,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) // are possible which would change it beforeDispatchCursor_ = safe.beforeDispatchCursor_; } - buffer()->undo().endUndoGroup(); + endUndoGroup(); // notify insets we just left if (*this != old) { @@ -415,14 +446,14 @@ DispatchResult const & Cursor::result() const BufferView & Cursor::bv() const { - LASSERT(bv_, /**/); + LBUFERR(bv_); return *bv_; } void Cursor::pop() { - LASSERT(depth() >= 1, /**/); + LBUFERR(depth() >= 1); pop_back(); } @@ -468,7 +499,7 @@ bool Cursor::popForward() int Cursor::currentMode() { - LASSERT(!empty(), /**/); + LASSERT(!empty(), return Inset::UNDECIDED_MODE); for (int i = depth() - 1; i >= 0; --i) { int res = operator[](i).inset().currentMode(); bool locked_mode = operator[](i).inset().lockedMode(); @@ -493,7 +524,6 @@ Row const & Cursor::textRow() const { CursorSlice const & cs = innerTextSlice(); ParagraphMetrics const & pm = bv().parMetrics(cs.text(), cs.pit()); - LASSERT(!pm.rows().empty(), /**/); return pm.getRow(pos(), boundary()); } @@ -505,27 +535,14 @@ void Cursor::resetAnchor() } -void Cursor::setCursorToAnchor() -{ - if (selection()) { - DocIterator normal = anchor_; - while (depth() < normal.depth()) - normal.pop_back(); - if (depth() < anchor_.depth() && top() <= anchor_[depth() - 1]) - ++normal.pos(); - setCursor(normal); - } -} - - -void Cursor::markEditPosition() +void Cursor::markNewWordPosition() { if (lyxrc.spellcheck_continuously && inTexted() && new_word_.empty()) { - FontSpan ow = locateWord(WHOLE_WORD); - if (ow.size() == 1) { + FontSpan nw = locateWord(WHOLE_WORD); + if (nw.size() == 1) { LYXERR(Debug::DEBUG, "start new word: " << " par: " << pit() - << " pos: " << ow.first); + << " pos: " << nw.first); new_word_ = *this; } } @@ -563,7 +580,7 @@ void Cursor::checkNewWordPosition() clearNewWordPosition(); else { FontSpan nw = locateWord(WHOLE_WORD); - if (nw.size()) { + if (!nw.empty()) { FontSpan ow = new_word_.locateWord(WHOLE_WORD); if (nw.intersect(ow).empty()) clearNewWordPosition(); @@ -602,7 +619,6 @@ bool Cursor::posVisRight(bool skip_inset) Cursor new_cur = *this; // where we will move to pos_type left_pos; // position visually left of current cursor pos_type right_pos; // position visually right of current cursor - bool new_pos_is_RTL; // is new position we're moving to RTL? getSurroundingPos(left_pos, right_pos); @@ -613,51 +629,58 @@ bool Cursor::posVisRight(bool skip_inset) new_cur.boundary(false); if (!skip_inset && text()->checkAndActivateInsetVisual(new_cur, right_pos >= pos(), false)) { - // we actually move the cursor at the end of this function, for now - // we just keep track of the new position in new_cur... + // we actually move the cursor at the end of this + // function, for now we just keep track of the new + // position in new_cur... LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos()); } // Are we already at rightmost pos in row? else if (text()->empty() || right_pos == -1) { - + new_cur = *this; if (!new_cur.posVisToNewRow(false)) { LYXERR(Debug::RTL, "not moving!"); return false; } - - // we actually move the cursor at the end of this function, for now - // just keep track of the new position in new_cur... - LYXERR(Debug::RTL, "right edge, moving: " << int(new_cur.pit()) << "," + + // we actually move the cursor at the end of this + // function, for now just keep track of the new + // position in new_cur... + LYXERR(Debug::RTL, "right edge, moving: " << int(new_cur.pit()) << "," << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0)); } // normal movement to the right else { new_cur = *this; - // Recall, if the cursor is at position 'x', that means *before* - // the character at position 'x'. In RTL, "before" means "to the - // right of", in LTR, "to the left of". So currently our situation - // is this: the position to our right is 'right_pos' (i.e., we're - // currently to the left of 'right_pos'). In order to move to the - // right, it depends whether or not the character at 'right_pos' is RTL. - new_pos_is_RTL = paragraph().getFontSettings( + // Recall, if the cursor is at position 'x', that + // means *before* the character at position 'x'. In + // RTL, "before" means "to the right of", in LTR, "to + // the left of". So currently our situation is this: + // the position to our right is 'right_pos' (i.e., + // we're currently to the left of 'right_pos'). In + // order to move to the right, it depends whether or + // not the character at 'right_pos' is RTL. + bool const new_pos_is_RTL = paragraph().getFontSettings( buffer()->params(), right_pos).isVisibleRightToLeft(); - // If the character at 'right_pos' *is* LTR, then in order to move to - // the right of it, we need to be *after* 'right_pos', i.e., move to - // position 'right_pos' + 1. + // If the character at 'right_pos' *is* LTR, then in + // order to move to the right of it, we need to be + // *after* 'right_pos', i.e., move to position + // 'right_pos' + 1. if (!new_pos_is_RTL) { new_cur.pos() = right_pos + 1; // set the boundary to true in two situations: if ( - // 1. if new_pos is now lastpos, and we're in an RTL paragraph - // (this means that we're moving right to the end of an LTR chunk + // 1. if new_pos is now lastpos, and we're in + // an RTL paragraph (this means that we're + // moving right to the end of an LTR chunk // which is at the end of an RTL paragraph); (new_cur.pos() == lastpos() && paragraph().isRTL(buffer()->params())) - // 2. if the position *after* right_pos is RTL (we want to be - // *after* right_pos, not before right_pos + 1!) + // 2. if the position *after* right_pos is RTL + // (we want to be *after* right_pos, not + // before right_pos + 1!) || paragraph().getFontSettings(buffer()->params(), new_cur.pos()).isVisibleRightToLeft() ) @@ -665,23 +688,23 @@ bool Cursor::posVisRight(bool skip_inset) else // set the boundary to false new_cur.boundary(false); } - // Otherwise (if the character at position 'right_pos' is RTL), then - // moving to the right of it is as easy as setting the new position - // to 'right_pos'. + // Otherwise (if the character at position 'right_pos' + // is RTL), then moving to the right of it is as easy + // as setting the new position to 'right_pos'. else { new_cur.pos() = right_pos; new_cur.boundary(false); } - + } bool moved = (new_cur.pos() != pos() || new_cur.pit() != pit() || new_cur.boundary() != boundary() || &new_cur.inset() != &inset()); - + if (moved) { - LYXERR(Debug::RTL, "moving to: " << new_cur.pos() + LYXERR(Debug::RTL, "moving to: " << new_cur.pos() << (new_cur.boundary() ? " (boundary)" : "")); *this = new_cur; } @@ -695,7 +718,6 @@ bool Cursor::posVisLeft(bool skip_inset) Cursor new_cur = *this; // where we will move to pos_type left_pos; // position visually left of current cursor pos_type right_pos; // position visually right of current cursor - bool new_pos_is_RTL; // is new position we're moving to RTL? getSurroundingPos(left_pos, right_pos); @@ -704,53 +726,60 @@ bool Cursor::posVisLeft(bool skip_inset) // Are we at an inset? new_cur.pos() = left_pos; new_cur.boundary(false); - if (!skip_inset && + if (!skip_inset && text()->checkAndActivateInsetVisual(new_cur, left_pos >= pos(), true)) { - // we actually move the cursor at the end of this function, for now - // we just keep track of the new position in new_cur... + // we actually move the cursor at the end of this + // function, for now we just keep track of the new + // position in new_cur... LYXERR(Debug::RTL, "entering inset at: " << new_cur.pos()); } // Are we already at leftmost pos in row? else if (text()->empty() || left_pos == -1) { - + new_cur = *this; if (!new_cur.posVisToNewRow(true)) { LYXERR(Debug::RTL, "not moving!"); return false; } - - // we actually move the cursor at the end of this function, for now - // just keep track of the new position in new_cur... - LYXERR(Debug::RTL, "left edge, moving: " << int(new_cur.pit()) << "," + + // we actually move the cursor at the end of this + // function, for now just keep track of the new + // position in new_cur... + LYXERR(Debug::RTL, "left edge, moving: " << int(new_cur.pit()) << "," << int(new_cur.pos()) << "," << (new_cur.boundary() ? 1 : 0)); } // normal movement to the left else { new_cur = *this; - // Recall, if the cursor is at position 'x', that means *before* - // the character at position 'x'. In RTL, "before" means "to the - // right of", in LTR, "to the left of". So currently our situation - // is this: the position to our left is 'left_pos' (i.e., we're - // currently to the right of 'left_pos'). In order to move to the - // left, it depends whether or not the character at 'left_pos' is RTL. - new_pos_is_RTL = paragraph().getFontSettings( + // Recall, if the cursor is at position 'x', that + // means *before* the character at position 'x'. In + // RTL, "before" means "to the right of", in LTR, "to + // the left of". So currently our situation is this: + // the position to our left is 'left_pos' (i.e., we're + // currently to the right of 'left_pos'). In order to + // move to the left, it depends whether or not the + // character at 'left_pos' is RTL. + bool const new_pos_is_RTL = paragraph().getFontSettings( buffer()->params(), left_pos).isVisibleRightToLeft(); - // If the character at 'left_pos' *is* RTL, then in order to move to - // the left of it, we need to be *after* 'left_pos', i.e., move to - // position 'left_pos' + 1. + // If the character at 'left_pos' *is* RTL, then in + // order to move to the left of it, we need to be + // *after* 'left_pos', i.e., move to position + // 'left_pos' + 1. if (new_pos_is_RTL) { new_cur.pos() = left_pos + 1; // set the boundary to true in two situations: if ( - // 1. if new_pos is now lastpos and we're in an LTR paragraph - // (this means that we're moving left to the end of an RTL chunk + // 1. if new_pos is now lastpos and we're in + // an LTR paragraph (this means that we're + // moving left to the end of an RTL chunk // which is at the end of an LTR paragraph); (new_cur.pos() == lastpos() && !paragraph().isRTL(buffer()->params())) - // 2. if the position *after* left_pos is not RTL (we want to be - // *after* left_pos, not before left_pos + 1!) + // 2. if the position *after* left_pos is not + // RTL (we want to be *after* left_pos, not + // before left_pos + 1!) || !paragraph().getFontSettings(buffer()->params(), new_cur.pos()).isVisibleRightToLeft() ) @@ -758,26 +787,26 @@ bool Cursor::posVisLeft(bool skip_inset) else // set the boundary to false new_cur.boundary(false); } - // Otherwise (if the character at position 'left_pos' is LTR), then - // moving to the left of it is as easy as setting the new position - // to 'left_pos'. + // Otherwise (if the character at position 'left_pos' + // is LTR), then moving to the left of it is as easy + // as setting the new position to 'left_pos'. else { new_cur.pos() = left_pos; new_cur.boundary(false); } - + } - bool moved = (new_cur.pos() != pos() + bool moved = (new_cur.pos() != pos() || new_cur.pit() != pit() || new_cur.boundary() != boundary()); if (moved) { - LYXERR(Debug::RTL, "moving to: " << new_cur.pos() + LYXERR(Debug::RTL, "moving to: " << new_cur.pos() << (new_cur.boundary() ? " (boundary)" : "")); *this = new_cur; } - + return moved; } @@ -793,50 +822,55 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) LYXERR(Debug::RTL, "bidi: " << row.pos() << "--" << row.endpos()); - // The cursor is painted *before* the character at pos(), or, if 'boundary' - // is true, *after* the character at (pos() - 1). So we already have one - // known position around the cursor: + // The cursor is painted *before* the character at pos(), or, + // if 'boundary' is true, *after* the character at (pos() - + // 1). So we already have one known position around the + // cursor: pos_type const known_pos = boundary() && pos() > 0 ? pos() - 1 : pos(); - - // edge case: if we're at the end of the paragraph, things are a little - // different (because lastpos is a position which does not really "exist" - // --- there's no character there yet). + + // edge case: if we're at the end of the paragraph, things are + // a little different (because lastpos is a position which + // does not really "exist" --- there's no character there + // yet). if (known_pos == lastpos()) { if (par.isRTL(buf.params())) { left_pos = -1; right_pos = bidi.vis2log(row.pos()); - } else { + } else { // LTR paragraph right_pos = -1; left_pos = bidi.vis2log(row.endpos() - 1); } return; } - - // Whether 'known_pos' is to the left or to the right of the cursor depends - // on whether it is an RTL or LTR character... - bool const cur_is_RTL = + + // Whether 'known_pos' is to the left or to the right of the + // cursor depends on whether it is an RTL or LTR character... + bool const cur_is_RTL = par.getFontSettings(buf.params(), known_pos).isVisibleRightToLeft(); // ... in the following manner: - // For an RTL character, "before" means "to the right" and "after" means - // "to the left"; and for LTR, it's the reverse. So, 'known_pos' is to the - // right of the cursor if (RTL && boundary) or (!RTL && !boundary): + // For an RTL character, "before" + // means "to the right" and "after" means "to the left"; and + // for LTR, it's the reverse. So, 'known_pos' is to the right + // of the cursor if (RTL && boundary) or (!RTL && !boundary): bool const known_pos_on_right = cur_is_RTL == boundary(); - // So we now know one of the positions surrounding the cursor. Let's - // determine the other one: + // So we now know one of the positions surrounding the cursor. + // Let's determine the other one: if (known_pos_on_right) { right_pos = known_pos; // *visual* position of 'left_pos': pos_type v_left_pos = bidi.log2vis(right_pos) - 1; - // If the position we just identified as 'left_pos' is a "skipped - // separator" (a separator which is at the logical end of a row, - // except for the last row in a paragraph; such separators are not - // painted, so they "are not really there"; note that in bidi text, - // such a separator could appear visually in the middle of a row), - // set 'left_pos' to the *next* position to the left. - if (bidi.inRange(v_left_pos) - && bidi.vis2log(v_left_pos) + 1 == row.endpos() + // If the position we just identified as 'left_pos' is + // a "skipped separator" (a separator which is at the + // logical end of a row, except for the last row in a + // paragraph; such separators are not painted, so they + // "are not really there"; note that in bidi text, + // such a separator could appear visually in the + // middle of a row), set 'left_pos' to the *next* + // position to the left. + if (bidi.inRange(v_left_pos) + && bidi.vis2log(v_left_pos) + 1 == row.endpos() && row.endpos() < lastpos() && par.isSeparator(bidi.vis2log(v_left_pos))) --v_left_pos; @@ -846,9 +880,10 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) left_pos = -1; else left_pos = bidi.vis2log(v_left_pos); - // If the position we identified as 'right_pos' is a "skipped - // separator", set 'right_pos' to the *next* position to the right. - if (right_pos + 1 == row.endpos() && row.endpos() < lastpos() + // If the position we identified as 'right_pos' is a + // "skipped separator", set 'right_pos' to the *next* + // position to the right. + if (right_pos + 1 == row.endpos() && row.endpos() < lastpos() && par.isSeparator(right_pos)) { pos_type const v_right_pos = bidi.log2vis(right_pos) + 1; if (!bidi.inRange(v_right_pos)) @@ -856,27 +891,29 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) else right_pos = bidi.vis2log(v_right_pos); } - } else { + } else { // known_pos is on the left left_pos = known_pos; // *visual* position of 'right_pos' pos_type v_right_pos = bidi.log2vis(left_pos) + 1; - // If the position we just identified as 'right_pos' is a "skipped - // separator", set 'right_pos' to the *next* position to the right. - if (bidi.inRange(v_right_pos) - && bidi.vis2log(v_right_pos) + 1 == row.endpos() + // If the position we just identified as 'right_pos' + // is a "skipped separator", set 'right_pos' to the + // *next* position to the right. + if (bidi.inRange(v_right_pos) + && bidi.vis2log(v_right_pos) + 1 == row.endpos() && row.endpos() < lastpos() && par.isSeparator(bidi.vis2log(v_right_pos))) ++v_right_pos; // calculate the logical position of 'right_pos', if in row - if (!bidi.inRange(v_right_pos)) + if (!bidi.inRange(v_right_pos)) right_pos = -1; else right_pos = bidi.vis2log(v_right_pos); - // If the position we identified as 'left_pos' is a "skipped - // separator", set 'left_pos' to the *next* position to the left. - if (left_pos + 1 == row.endpos() && row.endpos() < lastpos() + // If the position we identified as 'left_pos' is a + // "skipped separator", set 'left_pos' to the *next* + // position to the left. + if (left_pos + 1 == row.endpos() && row.endpos() < lastpos() && par.isSeparator(left_pos)) { pos_type const v_left_pos = bidi.log2vis(left_pos) - 1; if (!bidi.inRange(v_left_pos)) @@ -896,16 +933,16 @@ bool Cursor::posVisToNewRow(bool movingLeft) Row const & row = textRow(); bool par_is_LTR = !par.isRTL(buf.params()); - // Inside a table, determining whether to move to the next or previous row - // should be done based on the table's direction. + // Inside a table, determining whether to move to the next or + // previous row should be done based on the table's direction. int s = depth() - 1; if (s >= 1 && (*this)[s].inset().asInsetTabular()) { par_is_LTR = !(*this)[s].inset().asInsetTabular()->isRightToLeft(*this); LYXERR(Debug::RTL, "Inside table! par_is_LTR=" << (par_is_LTR ? 1 : 0)); } - - // if moving left in an LTR paragraph or moving right in an RTL one, - // move to previous row + + // if moving left in an LTR paragraph or moving right in an + // RTL one, move to previous row if (par_is_LTR == movingLeft) { if (row.pos() == 0) { // we're at first row in paragraph if (pit() == 0) // no previous paragraph! don't move @@ -919,8 +956,8 @@ bool Cursor::posVisToNewRow(bool movingLeft) boundary(false); } } - // if moving left in an RTL paragraph or moving right in an LTR one, - // move to next row + // if moving left in an RTL paragraph or moving right in an + // LTR one, move to next row else { if (row.endpos() == lastpos()) { // we're at last row in paragraph if (pit() == lastpit()) // last paragraph! don't move @@ -934,7 +971,7 @@ bool Cursor::posVisToNewRow(bool movingLeft) boundary(false); } } - + // make sure we're at left-/right-most pos in new row posVisToRowExtremity(!movingLeft); @@ -942,7 +979,7 @@ bool Cursor::posVisToNewRow(bool movingLeft) } -void Cursor::posVisToRowExtremity(bool left) +void Cursor::posVisToRowExtremity(bool left) { // prepare bidi tables Paragraph const & par = paragraph(); @@ -962,31 +999,44 @@ void Cursor::posVisToRowExtremity(bool left) else { pos() = bidi.vis2log(row.pos()); - // Moving to the leftmost position in the row, the cursor should - // normally be placed to the *left* of the leftmost position. - // A very common exception, though, is if the leftmost character - // also happens to be the separator at the (logical) end of the row - // --- in this case, the separator is positioned beyond the left - // margin, and we don't want to move the cursor there (moving to - // the left of the separator is equivalent to moving to the next - // line). So, in this case we actually want to place the cursor - // to the *right* of the leftmost position (the separator). - // Another exception is if we're moving to the logically last - // position in the row, which is *not* a separator: this means - // that the entire row has no separators (if there were any, the - // row would have been broken there); and therefore in this case - // we also move to the *right* of the last position (this indicates - // to the user that there is no space after this position, and is - // consistent with the behavior in the middle of a row --- moving - // right or left moves to the next/previous character; if we were - // to move to the *left* of this position, that would simulate - // a separator which is not really there!). - // Finally, there is an exception to the previous exception: if - // this non-separator-but-last-position-in-row is an inset, then - // we *do* want to stay to the left of it anyway: this is the + // Moving to the leftmost position in the row, + // the cursor should normally be placed to the + // *left* of the leftmost position. A very + // common exception, though, is if the + // leftmost character also happens to be the + // separator at the (logical) end of the row + // --- in this case, the separator is + // positioned beyond the left margin, and we + // don't want to move the cursor there (moving + // to the left of the separator is equivalent + // to moving to the next line). So, in this + // case we actually want to place the cursor + // to the *right* of the leftmost position + // (the separator). Another exception is if + // we're moving to the logically last position + // in the row, which is *not* a separator: + // this means that the entire row has no + // separators (if there were any, the row + // would have been broken there); and + // therefore in this case we also move to the + // *right* of the last position (this + // indicates to the user that there is no + // space after this position, and is + // consistent with the behavior in the middle + // of a row --- moving right or left moves to + // the next/previous character; if we were to + // move to the *left* of this position, that + // would simulate a separator which is not + // really there!). Finally, there is an + // exception to the previous exception: if + // this non-separator-but-last-position-in-row + // is an inset, then we *do* want to stay to + // the left of it anyway: this is the // "boundary" which we simulate at insets. - // Another exception is when row.endpos() is 0. - + + // Another exception is when row.endpos() is + // 0. + // do we want to be to the right of pos? // as explained above, if at last pos in row, stay to the right bool const right_of_pos = row.endpos() > 0 @@ -994,7 +1044,7 @@ void Cursor::posVisToRowExtremity(bool left) // Now we know if we want to be to the left or to the right of pos, // let's make sure we are where we want to be. - bool const new_pos_is_RTL = + bool const new_pos_is_RTL = par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft(); if (new_pos_is_RTL != right_of_pos) { @@ -1002,7 +1052,7 @@ void Cursor::posVisToRowExtremity(bool left) boundary(true); } } - } else { + } else { // move to rightmost position // if this is an LTR paragraph, and we're at the last row in the // paragraph, move to lastpos @@ -1011,31 +1061,42 @@ void Cursor::posVisToRowExtremity(bool left) else { pos() = row.endpos() > 0 ? bidi.vis2log(row.endpos() - 1) : 0; - // Moving to the rightmost position in the row, the cursor should - // normally be placed to the *right* of the rightmost position. - // A very common exception, though, is if the rightmost character - // also happens to be the separator at the (logical) end of the row - // --- in this case, the separator is positioned beyond the right - // margin, and we don't want to move the cursor there (moving to - // the right of the separator is equivalent to moving to the next - // line). So, in this case we actually want to place the cursor - // to the *left* of the rightmost position (the separator). - // Another exception is if we're moving to the logically last - // position in the row, which is *not* a separator: this means - // that the entire row has no separators (if there were any, the - // row would have been broken there); and therefore in this case - // we also move to the *left* of the last position (this indicates - // to the user that there is no space after this position, and is - // consistent with the behavior in the middle of a row --- moving - // right or left moves to the next/previous character; if we were - // to move to the *right* of this position, that would simulate - // a separator which is not really there!). - // Finally, there is an exception to the previous exception: if - // this non-separator-but-last-position-in-row is an inset, then - // we *do* want to stay to the right of it anyway: this is the - // "boundary" which we simulate at insets. - // Another exception is when row.endpos() is 0. - + // Moving to the rightmost position in the + // row, the cursor should normally be placed + // to the *right* of the rightmost position. A + // very common exception, though, is if the + // rightmost character also happens to be the + // separator at the (logical) end of the row + // --- in this case, the separator is + // positioned beyond the right margin, and we + // don't want to move the cursor there (moving + // to the right of the separator is equivalent + // to moving to the next line). So, in this + // case we actually want to place the cursor + // to the *left* of the rightmost position + // (the separator). Another exception is if + // we're moving to the logically last position + // in the row, which is *not* a separator: + // this means that the entire row has no + // separators (if there were any, the row + // would have been broken there); and + // therefore in this case we also move to the + // *left* of the last position (this indicates + // to the user that there is no space after + // this position, and is consistent with the + // behavior in the middle of a row --- moving + // right or left moves to the next/previous + // character; if we were to move to the + // *right* of this position, that would + // simulate a separator which is not really + // there!). Finally, there is an exception to + // the previous exception: if this + // non-separator-but-last-position-in-row is + // an inset, then we *do* want to stay to the + // right of it anyway: this is the "boundary" + // which we simulate at insets. Another + // exception is when row.endpos() is 0. + // do we want to be to the left of pos? // as explained above, if at last pos in row, stay to the left, // unless the last position is the same as the first. @@ -1044,7 +1105,7 @@ void Cursor::posVisToRowExtremity(bool left) // Now we know if we want to be to the left or to the right of pos, // let's make sure we are where we want to be. - bool const new_pos_is_RTL = + bool const new_pos_is_RTL = par.getFontSettings(buf.params(), pos()).isVisibleRightToLeft(); if (new_pos_is_RTL == left_of_pos) { @@ -1062,7 +1123,12 @@ CursorSlice Cursor::normalAnchor() const { if (!selection()) return top(); - LASSERT(anchor_.depth() >= depth(), /**/); + // LASSERT: There have been several bugs around this code, that seem + // to involve failures to reset the anchor. We can at least not crash + // in release mode by resetting it ourselves. + LASSERT(anchor_.depth() >= depth(), + const_cast(anchor_) = *this); + CursorSlice normal = anchor_[depth() - 1]; if (depth() < anchor_.depth() && top() <= normal) { // anchor is behind cursor -> move anchor behind the inset @@ -1135,10 +1201,8 @@ DocIterator Cursor::selectionEnd() const void Cursor::setSelection() { setSelection(true); - // A selection with no contents is not a selection - // FIXME: doesnt look ok - if (idx() == normalAnchor().idx() && - pit() == normalAnchor().pit() && + if (idx() == normalAnchor().idx() && + pit() == normalAnchor().pit() && pos() == normalAnchor().pos()) setSelection(false); } @@ -1240,7 +1304,8 @@ ostream & operator<<(ostream & os, Cursor const & cur) os << "------------------------------- | " << cur.anchor_[i] << "\n"; } os << " selection: " << cur.selection_ - << " x_target: " << cur.x_target_ << endl; + << " x_target: " << cur.x_target_ + << " boundary: " << cur.boundary() << endl; return os; } @@ -1363,7 +1428,7 @@ void Cursor::insert(docstring const & str) void Cursor::insert(char_type c) { //lyxerr << "Cursor::insert char '" << c << "'" << endl; - LASSERT(!empty(), /**/); + LASSERT(!empty(), return); if (inMathed()) { cap::selClearOrDel(*this); insert(new InsetMathChar(c)); @@ -1384,7 +1449,7 @@ void Cursor::insert(MathAtom const & t) void Cursor::insert(Inset * inset0) { - LASSERT(inset0, /**/); + LASSERT(inset0, return); if (inMathed()) insert(MathAtom(inset0->asInsetMath())); else { @@ -1451,8 +1516,6 @@ void Cursor::insert(MathData const & ar) bool Cursor::backspace() { - autocorrect() = false; - if (selection()) { cap::eraseSelection(*this); return true; @@ -1503,7 +1566,6 @@ bool Cursor::backspace() bool Cursor::erase() { - autocorrect() = false; if (inMacroMode()) return true; @@ -1558,7 +1620,6 @@ bool Cursor::up() if (disp_.dispatched()) return true; setCursor(save); - autocorrect() = false; return false; } @@ -1572,7 +1633,6 @@ bool Cursor::down() if (disp_.dispatched()) return true; setCursor(save); - autocorrect() = false; return false; } @@ -1612,7 +1672,7 @@ bool Cursor::macroModeClose() // we have to resolve the macro here manually and check its arity // to put the selection behind it if arity > 0. MacroData const * data = buffer()->getMacro(atomAsMacro->name()); - if (selection.size() > 0 && data && data->numargs() - data->optionals() > 0) { + if (!selection.empty() && data && data->numargs() - data->optionals() > 0) { macroArg = true; atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1); } else @@ -1623,7 +1683,7 @@ bool Cursor::macroModeClose() // insert remembered selection into first argument of a non-macro else if (atom.nucleus()->nargs() > 0) atom.nucleus()->cell(0).append(selection); - + plainInsert(atom); // finally put the macro argument behind, if needed @@ -1633,7 +1693,7 @@ bool Cursor::macroModeClose() else insert(selection); } - + return true; } @@ -1770,7 +1830,7 @@ bool Cursor::upDownInMath(bool up) else if (inset().asInsetText() && xo - textTargetOffset() != x_target()) { // In text mode inside the line (not left or right) possibly set a new target_x, // but only if we are somewhere else than the previous target-offset. - + // We want to keep the x-target on subsequent up/down movements // that cross beyond the end of short lines. Thus a special // handling when the cursor is at the end of line: Use the new @@ -1806,7 +1866,7 @@ bool Cursor::upDownInMath(bool up) push(*const_cast(p)); idx() = p->idxOfScript(up); pos() = lastpos(); - + // we went in the right direction? Otherwise don't jump into the script int x; int y; @@ -1819,7 +1879,7 @@ bool Cursor::upDownInMath(bool up) return true; } } - + // try right if (pos() != lastpos()) { InsetMathScript const * p = nextAtom()->asScriptInset(); @@ -1827,7 +1887,7 @@ bool Cursor::upDownInMath(bool up) push(*const_cast(p)); idx() = p->idxOfScript(up); pos() = 0; - + // we went in the right direction? Otherwise don't jump into the script int x; int y; @@ -1841,7 +1901,7 @@ bool Cursor::upDownInMath(bool up) } } } - + // try to find an inset that knows better then we, if (inset().idxUpDown(*this, up)) { //lyxerr << "idxUpDown triggered" << endl; @@ -1850,7 +1910,7 @@ bool Cursor::upDownInMath(bool up) setCursor(bruteFind2(*this, xo, yo)); return true; } - + // any improvement going just out of inset? if (popBackward() && inMathed()) { //lyxerr << "updown: popBackward succeeded" << endl; @@ -1861,7 +1921,7 @@ bool Cursor::upDownInMath(bool up) if (up ? ynew < yold : ynew > yold) return true; } - + // no success, we are probably at the document top or bottom operator=(old); return false; @@ -1872,13 +1932,13 @@ bool Cursor::atFirstOrLastRow(bool up) { TextMetrics const & tm = bv_->textMetrics(text()); ParagraphMetrics const & pm = tm.parMetrics(pit()); - + int row; if (pos() && boundary()) row = pm.pos2row(pos() - 1); else row = pm.pos2row(pos()); - + if (up) { if (pit() == 0 && row == 0) return true; @@ -1890,9 +1950,10 @@ bool Cursor::atFirstOrLastRow(bool up) return false; } + bool Cursor::upDownInText(bool up, bool & updateNeeded) { - LASSERT(text(), /**/); + LASSERT(text(), return false); // where are we? int xo = 0; @@ -1907,14 +1968,16 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) setTargetX(xo); else if (xo - textTargetOffset() != x_target() && depth() == beforeDispatchCursor_.depth()) { - // In text mode inside the line (not left or right) possibly set a new target_x, - // but only if we are somewhere else than the previous target-offset. - - // We want to keep the x-target on subsequent up/down movements - // that cross beyond the end of short lines. Thus a special - // handling when the cursor is at the end of line: Use the new - // x-target only if the old one was before the end of line - // or the old one was after the beginning of the line + // In text mode inside the line (not left or right) + // possibly set a new target_x, but only if we are + // somewhere else than the previous target-offset. + + // We want to keep the x-target on subsequent up/down + // movements that cross beyond the end of short lines. + // Thus a special handling when the cursor is at the + // end of line: Use the new x-target only if the old + // one was before the end of line or the old one was + // after the beginning of the line bool inRTL = isWithinRtlParagraph(*this); bool left; bool right; @@ -1933,7 +1996,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) xo = targetX(); } else xo = targetX(); - + // first get the current line TextMetrics & tm = bv_->textMetrics(text()); ParagraphMetrics const & pm = tm.parMetrics(pit()); @@ -1942,7 +2005,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) row = pm.pos2row(pos() - 1); else row = pm.pos2row(pos()); - + if (atFirstOrLastRow(up)) { // Is there a place for the cursor to go ? If yes, we // can execute the DEPM, otherwise we should keep the @@ -1955,11 +2018,11 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) break; } - // will a next dispatch follow and if there is a new + // will a next dispatch follow and if there is a new // dispatch will it move the cursor out ? if (depth() > 1 && valid_destination) { // The cursor hasn't changed yet. This happens when - // you e.g. move out of an inset. And to give the + // you e.g. move out of an inset. And to give the // DEPM the possibility of doing something we must // provide it with two different cursors. (Lgb, vfr) dummy = *this; @@ -1984,17 +2047,17 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) else tm.editXY(*this, xo, yo + textRow().descent() + 1); clearSelection(); - - // This happens when you move out of an inset. - // And to give the DEPM the possibility of doing - // something we must provide it with two different - // cursors. (Lgb) + + // This happens when you move out of an inset. + // And to give the DEPM the possibility of doing + // something we must provide it with two different + // cursors. (Lgb) Cursor dummy = *this; if (dummy == old) ++dummy.pos(); if (bv().checkDepm(dummy, old)) { updateNeeded = true; - // Make sure that cur gets back whatever happened to dummy (Lgb) + // Make sure that cur gets back whatever happened to dummy (Lgb) operator=(dummy); } } else { @@ -2024,17 +2087,11 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) next_row = 0; } } - top().pos() = min(tm.x2pos(pit(), next_row, xo), top().lastpos()); - int const xpos = tm.x2pos(pit(), next_row, xo); - bool const at_end_row = xpos == tm.x2pos(pit(), next_row, tm.width()); - bool const at_beg_row = xpos == tm.x2pos(pit(), next_row, 0); - - if (at_end_row && at_beg_row) - // make sure the cursor ends up on this row - boundary(false); - else - boundary(at_end_row); + Row const & real_next_row = tm.parMetrics(pit()).rows()[next_row]; + bool bound = false; + top().pos() = tm.getPosNearX(real_next_row, xo, bound); + boundary(bound); updateNeeded |= bv().checkDepm(*this, old); } @@ -2043,7 +2100,7 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) forceBufferUpdate(); updateTextTargetOffset(); return true; -} +} void Cursor::handleFont(string const & font) @@ -2098,16 +2155,18 @@ void Cursor::errorMessage(docstring const & msg) const namespace { + docstring parbreak(Cursor const * cur) { odocstringstream os; os << '\n'; // only add blank line if we're not in a ParbreakIsNewline situation - if (!cur->inset().getLayout().parbreakIsNewline() + if (!cur->inset().getLayout().parbreakIsNewline() && !cur->paragraph().layout().parbreak_is_newline) os << '\n'; return os.str(); } + } @@ -2237,30 +2296,30 @@ void Cursor::noScreenUpdate() const Font Cursor::getFont() const { - // The logic here should more or less match to the Cursor::setCurrentFont - // logic, i.e. the cursor height should give a hint what will happen - // if a character is entered. - + // The logic here should more or less match to the + // Cursor::setCurrentFont logic, i.e. the cursor height should + // give a hint what will happen if a character is entered. + // HACK. far from being perfect... CursorSlice const & sl = innerTextSlice(); Text const & text = *sl.text(); Paragraph const & par = text.getPar(sl.pit()); - + // on boundary, so we are really at the character before pos_type pos = sl.pos(); if (pos > 0 && boundary()) --pos; - + // on space? Take the font before (only for RTL boundary stay) if (pos > 0) { TextMetrics const & tm = bv().textMetrics(&text); if (pos == sl.lastpos() - || (par.isSeparator(pos) + || (par.isSeparator(pos) && !tm.isRTLBoundary(sl.pit(), pos))) --pos; } - + // get font at the position Font font = par.getFont(buffer()->params(), pos, text.outerFont(sl.pit())); @@ -2273,7 +2332,7 @@ bool Cursor::fixIfBroken() { bool const broken_cursor = DocIterator::fixIfBroken(); bool const broken_anchor = anchor_.fixIfBroken(); - + if (broken_cursor || broken_anchor) { clearNewWordPosition(); clearSelection(); @@ -2283,6 +2342,14 @@ bool Cursor::fixIfBroken() } +void Cursor::sanitize() +{ + setBuffer(&bv_->buffer()); + DocIterator::sanitize(); + anchor_.sanitize(); +} + + bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur) { // find inset in common @@ -2315,7 +2382,7 @@ bool notifyCursorLeavesOrEnters(Cursor const & old, Cursor & cur) if (cur[i].inset().notifyCursorEnters(cur)) return true; } - + return false; } @@ -2368,28 +2435,18 @@ void Cursor::setCurrentFont() bool Cursor::textUndo() { - DocIterator dit = *this; - // Undo::textUndo() will modify dit. - if (!buffer()->undo().textUndo(dit)) + if (!buffer()->undo().textUndo(*this)) return false; - // Set cursor - setCursor(dit); - clearSelection(); - fixIfBroken(); + sanitize(); return true; } bool Cursor::textRedo() { - DocIterator dit = *this; - // Undo::textRedo() will modify dit. - if (!buffer()->undo().textRedo(dit)) + if (!buffer()->undo().textRedo(*this)) return false; - // Set cursor - setCursor(dit); - clearSelection(); - fixIfBroken(); + sanitize(); return true; } @@ -2408,7 +2465,7 @@ void Cursor::beginUndoGroup() const void Cursor::endUndoGroup() const { - buffer()->undo().endUndoGroup(); + buffer()->undo().endUndoGroup(*this); } @@ -2461,7 +2518,7 @@ void Cursor::checkBufferStructure() Buffer const * master = buffer()->masterBuffer(); master->tocBackend().updateItem(*this); if (master != buffer() && !master->hasGuiDelegate()) - // In case the master has no gui associated with it, + // In case the master has no gui associated with it, // the TocItem is not updated (part of bug 5699). buffer()->tocBackend().updateItem(*this); }