#include <config.h>
-#include "Bidi.h"
#include "Buffer.h"
#include "BufferView.h"
#include "CoordCache.h"
}
+bool Cursor::inCoordCache() const
+{
+ // the root inset is not in cache, but we do not need it.
+ if (depth() == 1)
+ return true;
+ CoordCache::Insets const & icache = bv_->coordCache().getInsets();
+ for (size_t i = 1 ; i < depth() ; ++i)
+ if (!icache.has(&(*this)[i].inset()))
+ return false;
+ return true;
+}
+
+
void Cursor::getPos(int & x, int & y) const
{
Point p = bv().getPos(*this);
}
- bool moved = (new_cur.pos() != pos()
- || new_cur.pit() != pit()
- || new_cur.boundary() != boundary()
- || &new_cur.inset() != &inset());
+ bool const moved = new_cur != *this || new_cur.boundary() != boundary();
if (moved) {
LYXERR(Debug::RTL, "moving to: " << new_cur.pos()
}
- bool moved = (new_cur.pos() != pos()
- || new_cur.pit() != pit()
- || new_cur.boundary() != boundary());
+ bool const moved = new_cur != *this || new_cur.boundary() != boundary();
if (moved) {
LYXERR(Debug::RTL, "moving to: " << new_cur.pos()
}
-void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos)
+namespace {
+
+// Return true on success
+bool findNonVirtual(Row const & row, Row::const_iterator & cit, bool onleft)
{
- // preparing bidi tables
- Paragraph const & par = paragraph();
- Buffer const & buf = *buffer();
+ if (onleft) {
+ while (cit != row.begin() && cit->isVirtual())
+ --cit;
+ } else {
+ while (cit != row.end() && cit->isVirtual())
+ ++cit;
+ }
+ return cit != row.end() && !cit->isVirtual();
+}
+
+}
+
+void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const
+{
+ // by default, we know nothing.
+ left_pos = -1;
+ right_pos = -1;
+
Row const & row = textRow();
- Bidi bidi;
- bidi.computeTables(par, buf, row);
-
- 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:
- 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).
- if (known_pos == lastpos()) {
- if (par.isRTL(buf.params())) {
- left_pos = -1;
- right_pos = bidi.vis2log(row.pos());
- } else {
- // LTR paragraph
- right_pos = -1;
- left_pos = bidi.vis2log(row.endpos() - 1);
- }
+ TextMetrics const & tm = bv_->textMetrics(text());
+ double dummy = 0;
+ Row::const_iterator cit = tm.findRowElement(row, pos(), boundary(), dummy);
+ // Handle the case of empty row
+ if (cit == row.end()) {
+ if (paragraph().isRTL(buffer()->params()))
+ right_pos = row.pos();
+ else
+ left_pos = row.pos() - 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 =
- 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):
- 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:
- 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()
- && row.endpos() < lastpos()
- && par.isSeparator(bidi.vis2log(v_left_pos)))
- --v_left_pos;
-
- // calculate the logical position of 'left_pos', if in row
- if (!bidi.inRange(v_left_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()
- && par.isSeparator(right_pos)) {
- pos_type const v_right_pos = bidi.log2vis(right_pos) + 1;
- if (!bidi.inRange(v_right_pos))
- right_pos = -1;
- else
- right_pos = bidi.vis2log(v_right_pos);
- }
- } 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()
- && 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))
- 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()
- && par.isSeparator(left_pos)) {
- pos_type const v_left_pos = bidi.log2vis(left_pos) - 1;
- if (!bidi.inRange(v_left_pos))
- left_pos = -1;
- else
- left_pos = bidi.vis2log(v_left_pos);
+ // skip virtual elements and exit if no non-virtual one exists
+ if (!findNonVirtual(row, cit, !cit->isRTL()))
+ return;
+
+ // if the position is at the left side of the element, we have to
+ // look at the previous element
+ if (pos() == cit->left_pos()) {
+ LYXERR(Debug::RTL, "getSurroundingPos(" << pos() << (boundary() ? "b" : "")
+ << "), AT LEFT of *cit=" << *cit);
+ // this one is easy (see common case below)
+ right_pos = pos() - (cit->isRTL() ? 1 : 0);
+ // at the left of the row
+ if (cit == row.begin())
+ return;
+ --cit;
+ if (!findNonVirtual(row, cit, true))
+ return;
+ // [...[ is the row element, | is cursor position (! with boundary)
+ // [ 1 2 [ is a ltr row element with pos=1 and endpos=3
+ // ] 2 1] is an rtl row element with pos=1 and endpos=3
+ // [ 1 2 [ [|3 4 [ => (2, 3)
+ // or [ 1 2 [ ]!4 3 ] => (2, 4)
+ // or ] 2 1 ] [|3 4 [ => (1, 3)
+ // or ] 4 3 ] ]!2 1 ] => (3, 2)
+ left_pos = cit->right_pos() - (cit->isRTL() ? 0 : 1);
+ // happens with consecutive row of same direction
+ if (left_pos == right_pos) {
+ left_pos += cit->isRTL() ? 1 : -1;
}
}
- return;
+ // same code but with the element at the right
+ else if (pos() == cit->right_pos()) {
+ LYXERR(Debug::RTL, "getSurroundingPos(" << pos() << (boundary() ? "b" : "")
+ << "), AT RIGHT of *cit=" << *cit);
+ // this one is easy (see common case below)
+ left_pos = pos() - (cit->isRTL() ? 0 : 1);
+ // at the right of the row
+ if (cit + 1 == row.end())
+ return;
+ ++cit;
+ if (!findNonVirtual(row, cit, false))
+ return;
+ // [ 1 2![ [ 3 4 [ => (2, 3)
+ // or [ 1 2![ ] 4 3 ] => (2, 4)
+ // or ] 2 1|] [ 3 4 [ => (1, 3)
+ // or ] 4 3|] ] 2 1 ] => (3, 2)
+ right_pos = cit->left_pos() - (cit->isRTL() ? 1 : 0);
+ // happens with consecutive row of same direction
+ if (right_pos == left_pos)
+ right_pos += cit->isRTL() ? -1 : 1;
+ }
+ // common case: both positions are inside the row element
+ else {
+ // [ 1 2|3 [ => (2, 3)
+ // or ] 3|2 1 ] => (3, 2)
+ left_pos = pos() - (cit->isRTL() ? 0 : 1);
+ right_pos = pos() - (cit->isRTL() ? 1 : 0);
+ }
+
+ // Note that debug message does not catch all early returns above
+ LYXERR(Debug::RTL,"getSurroundingPos(" << pos() << (boundary() ? "b" : "")
+ << ") => (" << left_pos << ", " << right_pos <<")");
}
// 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);
+ if (inset().asInsetTabular()) {
+ par_is_LTR = !inset().asInsetTabular()->isRightToLeft(*this);
LYXERR(Debug::RTL, "Inside table! par_is_LTR=" << (par_is_LTR ? 1 : 0));
}
// 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<DocIterator &>(anchor_) = *this);
+ if (anchor_.depth() < depth()) {
+ LYXERR0("Cursor is deeper than anchor. PLEASE REPORT.\nCursor is"
+ << *this);
+ const_cast<DocIterator &>(anchor_) = *this;
+ }
CursorSlice normal = anchor_[depth() - 1];
if (depth() < anchor_.depth() && top() <= normal) {
if (inset)
prevInset()->infoize2(os);
}
- // overwite old message
- os << " ";
}
++pos();
inset().setBuffer(bv_->buffer());
inset().initView();
- forceBufferUpdate();
+ checkBufferStructure();
}
<< pos() << ' ' << lastpos() << " in idx: " << idx()
<< " in atom: '";
odocstringstream os;
- WriteStream wi(os, false, true, WriteStream::wsDefault);
+ TexRow texrow(false);
+ otexrowstream ots(os,texrow);
+ WriteStream wi(ots, false, true, WriteStream::wsDefault);
inset().asInsetMath()->write(wi);
lyxerr << to_utf8(os.str()) << endl;
pos() = lastpos();
// Make sure that cur gets back whatever happened to dummy (Lgb)
operator=(dummy);
}
+ if (inTexted() && pos() && paragraph().isEnvSeparator(pos() - 1))
+ posBackward();
} else {
// if there is a selection, we stay out of any inset,
// and just jump to the right position:
if (inMathed()) {
odocstringstream os;
info(os);
+#ifdef DEVEL_VERSION
+ InsetMath * math = inset().asInsetMath();
+ if (math)
+ os << _(", Inset: ") << math->id();
+ os << _(", Cell: ") << idx();
+ os << _(", Position: ") << pos();
+#endif
return os.str();
}
// In case the master has no gui associated with it,
// the TocItem is not updated (part of bug 5699).
buffer()->tocBackend().updateItem(*this);
+
+ // If the last tracked change of the paragraph has just been
+ // deleted, then we need to recompute the buffer flag
+ // tracked_changes_present_.
+ if (inTexted() && paragraph().isChangeUpdateRequired())
+ disp_.forceChangesUpdate();
}