X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FCursor.cpp;h=eff32d05769bf650b7f6846c586efdaf19fa5267;hb=28be7d552f62cc02fa86d7f79201d089bfb2d7b5;hp=b13b6a56f7f497e973e237770159cecfa834e94d;hpb=3fd2398a28c8d2c85d1bb2c33c6ca15bfc398b54;p=lyx.git diff --git a/src/Cursor.cpp b/src/Cursor.cpp index b13b6a56f7..eff32d0576 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -15,6 +15,7 @@ #include #include "Buffer.h" +#include "BufferParams.h" #include "BufferView.h" #include "CoordCache.h" #include "Cursor.h" @@ -194,13 +195,37 @@ void Cursor::reset() } -// this (intentionally) does neither touch anchor nor selection status void Cursor::setCursor(DocIterator const & cur) { DocIterator::operator=(cur); } +void Cursor::setCursorSelectionTo(DocIterator dit) +{ + size_t i = 0; + // normalise dit + while (i < dit.depth() && i < anchor_.depth() && dit[i] == anchor_[i]) + ++i; + if (i != dit.depth()) { + // otherwise the cursor is already normal + if (i == anchor_.depth()) + // dit is a proper extension of the anchor_ + dit.cutOff(i - 1); + else if (i + 1 < dit.depth()) { + // one has dit[i] != anchor_[i] but either dit[i-1] == anchor_[i-1] + // or i == 0. Remove excess. + dit.cutOff(i); + if (dit[i] > anchor_[i]) + // place dit after the inset it was in + ++dit.pos(); + } + } + setCursor(dit); + setSelection(); +} + + void Cursor::setCursorToAnchor() { if (selection()) { @@ -526,24 +551,6 @@ void Cursor::checkNewWordPosition() } -bool Cursor::posBackward() -{ - if (pos() == 0) - return false; - --pos(); - return true; -} - - -bool Cursor::posForward() -{ - if (pos() == lastpos()) - return false; - ++pos(); - return true; -} - - bool Cursor::posVisRight(bool skip_inset) { Cursor new_cur = *this; // where we will move to @@ -760,12 +767,11 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const right_pos = -1; Row const & row = textRow(); - TextMetrics const & tm = bv_->textMetrics(text()); double dummy = 0; - Row::const_iterator cit = tm.findRowElement(row, pos(), boundary(), dummy); + Row::const_iterator cit = row.findElement(pos(), boundary(), dummy); // Handle the case of empty row if (cit == row.end()) { - if (paragraph().isRTL(buffer()->params())) + if (row.isRTL()) right_pos = row.pos(); else left_pos = row.pos() - 1; @@ -839,10 +845,8 @@ void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const bool Cursor::posVisToNewRow(bool movingLeft) { - Paragraph const & par = paragraph(); - Buffer const & buf = *buffer(); Row const & row = textRow(); - bool par_is_LTR = !par.isRTL(buf.params()); + bool par_is_LTR = !row.isRTL(); // Inside a table, determining whether to move to the next or // previous row should be done based on the table's direction. @@ -1279,7 +1283,7 @@ void Cursor::insert(MathData const & ar) } -bool Cursor::backspace() +bool Cursor::backspace(bool const force) { if (selection()) { cap::eraseSelection(*this); @@ -1315,7 +1319,7 @@ bool Cursor::backspace() } } - if (pos() != 0 && prevAtom()->nargs() > 0) { + if (pos() != 0 && !force && prevAtom()->confirmDeletion()) { // let's require two backspaces for 'big stuff' and // highlight on the first resetAnchor(); @@ -1329,7 +1333,7 @@ bool Cursor::backspace() } -bool Cursor::erase() +bool Cursor::erase(bool const force) { if (inMacroMode()) return true; @@ -1364,7 +1368,7 @@ bool Cursor::erase() } // 'clever' UI hack: only erase large items if previously slected - if (pos() != lastpos() && nextAtom()->nargs() > 0) { + if (pos() != lastpos() && !force && nextAtom()->confirmDeletion()) { resetAnchor(); selection(true); ++pos(); @@ -1455,7 +1459,7 @@ bool Cursor::macroModeClose() bool keep_mathmode = it != words.end() && (it->second.inset == "font" || it->second.inset == "oldfont" || it->second.inset == "mbox"); - bool ert_macro = !user_macro && it == words.end(); + bool ert_macro = !user_macro && it == words.end() && atomAsMacro; if (in && in->currentMode() == Inset::TEXT_MODE && atom.nucleus()->currentMode() == Inset::MATH_MODE @@ -1717,6 +1721,95 @@ bool Cursor::upDownInMath(bool up) } +InsetMath & Cursor::nextMath() +{ + return *nextAtom().nucleus(); +} + + +InsetMath & Cursor::prevMath() +{ + return *prevAtom().nucleus(); +} + + +bool Cursor::mathForward(bool word) +{ + LASSERT(inMathed(), return false); + if (pos() < lastpos()) { + if (word) { + // word: skip a group of insets of the form X*(B*|R*|P*) (greedy + // match) where X is any math class, B is mathbin, R is mathrel, and + // P is mathpunct. Make sure that the following remains true: + // mathForward(true); mathBackward(true); mathForward(true) + // is the same as mathForward(true) and + // mathBackward(true); mathForward(true); mathBackward(true) + // is the same as mathBackward(true). + MathClass mc = nextMath().mathClass(); + do + posForward(); + while (pos() < lastpos() && mc == nextMath().mathClass()); + if (pos() < lastpos() && + ((mc = nextMath().mathClass()) == MC_BIN || + mc == MC_REL || mc == MC_PUNCT)) + do + posForward(); + while (pos() < lastpos() && mc == nextMath().mathClass()); + } else if (openable(nextAtom())) { + // single step: try to enter the next inset + pushBackward(nextMath()); + inset().idxFirst(*this); + } else + posForward(); + return true; + } + if (inset().idxForward(*this)) + return true; + // try to pop forwards --- but don't pop out of math! leave that to + // the FINISH lfuns + int s = depth() - 2; + if (s >= 0 && operator[](s).inset().asInsetMath()) + return popForward(); + return false; +} + + +bool Cursor::mathBackward(bool word) +{ + LASSERT(inMathed(), return false); + if (pos() > 0) { + if (word) { + // word: skip a group of insets. See the comment in mathForward. + MathClass mc = prevMath().mathClass(); + do + posBackward(); + while (pos() > 0 && mc == prevMath().mathClass()); + if (pos() > 0 && (mc == MC_BIN || mc == MC_REL || mc == MC_PUNCT)) { + mc = prevMath().mathClass(); + do + posBackward(); + while (pos() > 0 && mc == prevMath().mathClass()); + } + } else if (openable(prevAtom())) { + // single step: try to enter the preceding inset + posBackward(); + push(nextMath()); + inset().idxLast(*this); + } else + posBackward(); + return true; + } + if (inset().idxBackward(*this)) + return true; + // try to pop backwards --- but don't pop out of math! leave that to + // the FINISH lfuns + int s = depth() - 2; + if (s >= 0 && operator[](s).inset().asInsetMath()) + return popBackward(); + return false; +} + + bool Cursor::atFirstOrLastRow(bool up) { TextMetrics const & tm = bv_->textMetrics(text()); @@ -1831,10 +1924,13 @@ bool Cursor::upDownInText(bool up, bool & updateNeeded) int yo = bv().getPos(*this).y_; Cursor old = *this; // To next/previous row + // FIXME: the y position is often guessed wrongly across styles and + // insets, which leads to weird behaviour. if (up) tm.editXY(*this, xo, yo - textRow().ascent() - 1); else tm.editXY(*this, xo, yo + textRow().descent() + 1); + x_target_ = old.x_target_; clearSelection(); // This happens when you move out of an inset. @@ -2042,10 +2138,14 @@ Encoding const * Cursor::getEncoding() const { if (empty()) return 0; + BufferParams const & bp = bv().buffer().params(); + if (bp.useNonTeXFonts) + return encodings.fromLyXName("utf8-plain"); + CursorSlice const & sl = innerTextSlice(); Text const & text = *sl.text(); - Font font = text.getPar(sl.pit()).getFont( - bv().buffer().params(), sl.pos(), text.outerFont(sl.pit())); + Font font = text.getPar(sl.pit()).getFont(bp, sl.pos(), + text.outerFont(sl.pit())); return font.language()->encoding(); } @@ -2097,6 +2197,7 @@ 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. + // FIXME: this is not the case, what about removing this method ? (see #10478). // HACK. far from being perfect... @@ -2144,6 +2245,7 @@ void Cursor::sanitize() { setBuffer(&bv_->buffer()); DocIterator::sanitize(); + new_word_.sanitize(); if (selection()) anchor_.sanitize(); else @@ -2337,4 +2439,39 @@ void Cursor::checkBufferStructure() } +bool Cursor::confirmDeletion(bool const before) const +{ + if (!selection()) { + if (Inset const * inset = before ? prevInset() : nextInset()) + return inset->confirmDeletion(); + } else { + DocIterator dit = selectionBegin(); + CursorSlice const end = selectionEnd().top(); + for (; dit.top() < end; dit.top().forwardPos()) + if (Inset const * inset = dit.nextInset()) + if (inset->confirmDeletion()) + return true; + } + return false; +} + + +void Cursor::moveToClosestEdge(int const x, bool const edit) +{ + if (Inset const * inset = nextInset()) { + // stay in front of insets for which we want to open the dialog + // (e.g. InsetMathSpace). + if (edit && (inset->hasSettings() || !inset->contextMenuName().empty())) + return; + CoordCache::Insets const & insetCache = bv().coordCache().getInsets(); + if (!insetCache.has(inset)) + return; + int const wid = insetCache.dim(inset).wid; + Point p = insetCache.xy(inset); + if (x > p.x_ + (wid + 1) / 2) + posForward(); + } +} + + } // namespace lyx