+ setCursor(dit);
+ setSelection();
+}
+
+
+void CursorData::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);
+ }
+}
+
+
+CursorSlice CursorData::normalAnchor() const
+{
+ if (!selection())
+ return top();
+ // 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.
+ 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) {
+ // anchor is behind cursor -> move anchor behind the inset
+ ++normal.pos();
+ }
+ return normal;
+}
+
+
+void CursorData::setSelection()
+{
+ selection(true);
+ if (idx() == normalAnchor().idx() &&
+ pit() == normalAnchor().pit() &&
+ pos() == normalAnchor().pos())
+ selection(false);
+}
+
+
+void CursorData::setSelection(DocIterator const & where, int n)
+{
+ setCursor(where);
+ selection(true);
+ anchor_ = where;
+ pos() += n;
+}
+
+
+void CursorData::resetAnchor()
+{
+ anchor_ = *this;
+ checkNewWordPosition();
+}
+
+
+CursorSlice CursorData::selBegin() const
+{
+ if (!selection())
+ return top();
+ return normalAnchor() < top() ? normalAnchor() : top();
+}
+
+
+CursorSlice CursorData::selEnd() const
+{
+ if (!selection())
+ return top();
+ return normalAnchor() > top() ? normalAnchor() : top();
+}
+
+
+DocIterator CursorData::selectionBegin() const
+{
+ if (!selection())
+ return *this;
+
+ DocIterator di;
+ // FIXME: This is a work-around for the problem that
+ // CursorSlice doesn't keep track of the boundary.
+ if (normalAnchor() == top())
+ di = anchor_.boundary() > boundary() ? anchor_ : *this;
+ else
+ di = normalAnchor() < top() ? anchor_ : *this;
+ di.resize(depth());
+ return di;
+}
+
+
+DocIterator CursorData::selectionEnd() const
+{
+ if (!selection())
+ return *this;
+
+ DocIterator di;
+ // FIXME: This is a work-around for the problem that
+ // CursorSlice doesn't keep track of the boundary.
+ if (normalAnchor() == top())
+ di = anchor_.boundary() < boundary() ? anchor_ : *this;
+ else
+ di = normalAnchor() > top() ? anchor_ : *this;
+
+ if (di.depth() > depth()) {
+ di.resize(depth());
+ ++di.pos();
+ }
+ return di;
+}
+
+
+namespace {
+
+docstring parbreak(CursorData const * cur)
+{
+ odocstringstream os;
+ os << '\n';
+ // only add blank line if we're not in a ParbreakIsNewline situation
+ if (!cur->inset().getLayout().parbreakIsNewline()
+ && !cur->paragraph().layout().parbreak_is_newline)
+ os << '\n';
+ return os.str();
+}
+
+}
+
+
+docstring CursorData::selectionAsString(bool with_label) const
+{
+ if (!selection())
+ return docstring();
+
+ if (inMathed())
+ return cap::grabSelection(*this);
+
+ int const label = with_label
+ ? AS_STR_LABEL | AS_STR_INSETS : AS_STR_INSETS;
+
+ idx_type const startidx = selBegin().idx();
+ idx_type const endidx = selEnd().idx();
+ if (startidx != endidx) {
+ // multicell selection
+ InsetTabular * table = inset().asInsetTabular();
+ LASSERT(table, return docstring());
+ return table->asString(startidx, endidx);
+ }
+
+ ParagraphList const & pars = text()->paragraphs();
+
+ pit_type const startpit = selBegin().pit();
+ pit_type const endpit = selEnd().pit();
+ size_t const startpos = selBegin().pos();
+ size_t const endpos = selEnd().pos();
+
+ if (startpit == endpit)
+ return pars[startpit].asString(startpos, endpos, label);
+
+ // First paragraph in selection
+ docstring result = pars[startpit].
+ asString(startpos, pars[startpit].size(), label)
+ + parbreak(this);
+
+ // The paragraphs in between (if any)
+ for (pit_type pit = startpit + 1; pit != endpit; ++pit) {
+ Paragraph const & par = pars[pit];
+ result += par.asString(0, par.size(), label)
+ + parbreak(this);
+ }
+
+ // Last paragraph in selection
+ result += pars[endpit].asString(0, endpos, label);
+
+ return result;
+}
+
+
+void CursorData::info(odocstream & os, bool devel_mode) const
+{
+ for (int i = 1, n = depth(); i < n; ++i) {
+ operator[](i).inset().infoize(os);
+ os << " ";
+ }
+ if (pos() != 0) {
+ Inset const * inset = prevInset();
+ // prevInset() can return 0 in certain case.
+ if (inset)
+ prevInset()->infoize2(os);
+ }
+ if (devel_mode) {
+ InsetMath * math = inset().asInsetMath();
+ if (math)
+ os << _(", Inset: ") << math->id();
+ os << _(", Cell: ") << idx();
+ os << _(", Position: ") << pos();
+ }
+
+}
+
+docstring CursorData::currentState(bool devel_mode) const
+{
+ if (inMathed()) {
+ odocstringstream os;
+ info(os, devel_mode);
+ return os.str();
+ }
+
+ if (inTexted())
+ return text()->currentState(*this, devel_mode);
+
+ return docstring();
+}
+
+
+void CursorData::markNewWordPosition()
+{
+ if (lyxrc.spellcheck_continuously && inTexted() && new_word_.empty()) {
+ FontSpan nw = locateWord(WHOLE_WORD);
+ if (nw.size() == 1) {
+ LYXERR(Debug::DEBUG, "start new word: "
+ << " par: " << pit()
+ << " pos: " << nw.first);
+ new_word_ = *this;
+ }
+ }
+}
+
+
+void CursorData::clearNewWordPosition()
+{
+ if (!new_word_.empty()) {
+ LYXERR(Debug::DEBUG, "clear new word: "
+ << " par: " << pit()
+ << " pos: " << pos());
+ new_word_.resize(0);
+ }
+}
+
+
+void CursorData::checkNewWordPosition()
+{
+ if (!lyxrc.spellcheck_continuously || new_word_.empty())
+ return ;
+ if (!inTexted())
+ clearNewWordPosition();
+ else {
+ // forget the position of the current new word if
+ // 1) the paragraph changes or
+ // 2) the count of nested insets changes or
+ // 3) the cursor pos is out of paragraph bound
+ if (pit() != new_word_.pit() ||
+ depth() != new_word_.depth() ||
+ new_word_.pos() > new_word_.lastpos()) {
+ clearNewWordPosition();
+ } else if (new_word_.fixIfBroken())
+ // 4) or the remembered position was "broken"
+ clearNewWordPosition();
+ else {
+ FontSpan nw = locateWord(WHOLE_WORD);
+ if (!nw.empty()) {
+ FontSpan ow = new_word_.locateWord(WHOLE_WORD);
+ if (nw.intersect(ow).empty())
+ clearNewWordPosition();
+ else
+ LYXERR(Debug::DEBUG, "new word: "
+ << " par: " << pit()
+ << " pos: " << nw.first << ".." << nw.last);
+ } else {
+ clearNewWordPosition();
+ }
+ }
+ }
+}
+
+
+void CursorData::clearSelection()
+{
+ selection(false);
+ setWordSelection(false);
+ setMark(false);
+ resetAnchor();
+}
+
+
+bool CursorData::fixIfBroken()
+{
+ bool const broken_cursor = DocIterator::fixIfBroken();
+ bool const broken_anchor = anchor_.fixIfBroken();
+
+ if (broken_cursor || broken_anchor) {
+ clearNewWordPosition();
+ clearSelection();
+ return true;
+ }
+ return false;
+}
+
+
+void CursorData::sanitize()
+{
+ DocIterator::sanitize();
+ if (selection())
+ anchor_.sanitize();
+ else
+ resetAnchor();
+}
+
+
+bool CursorData::isInside(Inset const * p) const
+{
+ for (size_t i = 0; i != depth(); ++i)
+ if (&operator[](i).inset() == p)
+ return true;
+ return false;
+}
+
+
+void CursorData::leaveInset(Inset const & inset)
+{
+ for (size_t i = 0; i != depth(); ++i) {
+ if (&operator[](i).inset() == &inset) {
+ resize(i);
+ return;
+ }
+ }
+}
+
+
+bool CursorData::textUndo()
+{
+ if (!buffer()->undo().textUndo(*this))
+ return false;
+ sanitize();
+ return true;
+}
+
+
+bool CursorData::textRedo()
+{
+ if (!buffer()->undo().textRedo(*this))
+ return false;
+ sanitize();
+ return true;
+}
+
+
+void CursorData::finishUndo() const
+{
+ buffer()->undo().finishUndo();
+}
+
+
+void CursorData::beginUndoGroup() const
+{
+ buffer()->undo().beginUndoGroup(*this);
+}
+
+
+void CursorData::endUndoGroup() const
+{
+ buffer()->undo().endUndoGroup(*this);
+}
+
+
+void CursorData::recordUndo(pit_type from, pit_type to) const
+{
+ buffer()->undo().recordUndo(*this, from, to);
+}
+
+
+void CursorData::recordUndo(pit_type from) const
+{
+ buffer()->undo().recordUndo(*this, from, pit());
+}
+
+
+void CursorData::recordUndo(UndoKind kind) const
+{
+ buffer()->undo().recordUndo(*this, kind);
+}
+
+
+void CursorData::recordUndoInset(Inset const * in) const
+{
+ buffer()->undo().recordUndoInset(*this, in);
+}
+
+
+void CursorData::recordUndoFullBuffer() const
+{
+ buffer()->undo().recordUndoFullBuffer(*this);
+}
+
+
+void CursorData::recordUndoBufferParams() const
+{
+ buffer()->undo().recordUndoBufferParams(*this);
+}
+
+
+void CursorData::recordUndoSelection() const
+{
+ if (inMathed()) {
+ if (cap::multipleCellsSelected(*this))
+ recordUndoInset();
+ else
+ recordUndo();
+ } else {
+ buffer()->undo().recordUndo(*this,
+ selBegin().pit(), selEnd().pit());
+ }
+}
+
+
+int CursorData::currentMode()
+{
+ 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();
+ // Also return UNDECIDED_MODE when the mode is locked,
+ // as in this case it is treated the same as TEXT_MODE
+ if (res != Inset::UNDECIDED_MODE || locked_mode)
+ return res;
+ }
+ return Inset::TEXT_MODE;