X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FCursor.cpp;h=a139a2b310fd2a1de6674e62b762a6abf78039f9;hb=c75d9bf7c4e1ec31b31be55ce3f88ba605cff625;hp=64d7ad3e5e833705dae3d7c31b8597f17c8fa7a9;hpb=a24033ff0e49316ba90919f024c561914d55ad47;p=lyx.git diff --git a/src/Cursor.cpp b/src/Cursor.cpp index 64d7ad3e5e..a139a2b310 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" @@ -52,7 +53,7 @@ #include "mathed/MacroTable.h" #include "mathed/MathData.h" #include "mathed/MathFactory.h" -#include "mathed/MathMacro.h" +#include "mathed/InsetMathMacro.h" #include #include @@ -114,7 +115,7 @@ DocIterator bruteFind(Cursor const & c, int x, int y) } -} // namespace anon +} // namespace CursorData::CursorData() @@ -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 @@ -751,7 +758,7 @@ bool findNonVirtual(Row const & row, Row::const_iterator & cit, bool onleft) return cit != row.end() && !cit->isVirtual(); } -} +} // namespace void Cursor::getSurroundingPos(pos_type & left_pos, pos_type & right_pos) const { @@ -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. @@ -1053,7 +1057,7 @@ void Cursor::updateTextTargetOffset() } -void Cursor::info(odocstream & os) const +void Cursor::info(odocstream & os, bool devel_mode) const { for (int i = 1, n = depth(); i < n; ++i) { operator[](i).inset().infoize(os); @@ -1065,6 +1069,14 @@ void Cursor::info(odocstream & os) const if (inset) prevInset()->infoize2(os); } + if (devel_mode) { + InsetMath * math = inset().asInsetMath(); + if (math) + os << _(", Inset: ") << math->id(); + os << _(", Cell: ") << idx(); + os << _(", Position: ") << pos(); + } + } @@ -1279,7 +1291,7 @@ void Cursor::insert(MathData const & ar) } -bool Cursor::backspace() +bool Cursor::backspace(bool const force) { if (selection()) { cap::eraseSelection(*this); @@ -1315,7 +1327,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 +1341,7 @@ bool Cursor::backspace() } -bool Cursor::erase() +bool Cursor::erase(bool const force) { if (inMacroMode()) return true; @@ -1364,7 +1376,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(); @@ -1426,12 +1438,13 @@ bool Cursor::macroModeClose() InsetMathNest * const in = inset().asInsetMath()->asNestInset(); if (in && in->interpretString(*this, s)) return true; - MathAtom atom = buffer()->getMacro(name, *this, false) ? - MathAtom(new MathMacro(buffer(), name)) : createInsetMath(name, buffer()); + bool const user_macro = buffer()->getMacro(name, *this, false); + MathAtom atom = user_macro ? MathAtom(new InsetMathMacro(buffer(), name)) + : createInsetMath(name, buffer()); // try to put argument into macro, if we just inserted a macro bool macroArg = false; - MathMacro * atomAsMacro = atom.nucleus()->asMacro(); + InsetMathMacro * atomAsMacro = atom.nucleus()->asMacro(); if (atomAsMacro) { // macros here are still unfolded (in init mode in fact). So // we have to resolve the macro here manually and check its arity @@ -1439,17 +1452,22 @@ bool Cursor::macroModeClose() MacroData const * data = buffer()->getMacro(atomAsMacro->name()); if (!selection.empty() && data && data->numargs() - data->optionals() > 0) { macroArg = true; - atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 1); + atomAsMacro->setDisplayMode(InsetMathMacro::DISPLAY_INTERACTIVE_INIT, 1); } else // non-greedy case. Do not touch the arguments behind - atomAsMacro->setDisplayMode(MathMacro::DISPLAY_INTERACTIVE_INIT, 0); + atomAsMacro->setDisplayMode(InsetMathMacro::DISPLAY_INTERACTIVE_INIT, 0); } // insert remembered selection into first argument of a non-macro else if (atom.nucleus()->nargs() > 0) atom.nucleus()->cell(0).append(selection); - bool ert_macro = atomAsMacro && !atomAsMacro->macro(); + MathWordList const & words = mathedWordList(); + MathWordList::const_iterator it = words.find(name); + 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() && atomAsMacro; if (in && in->currentMode() == Inset::TEXT_MODE && atom.nucleus()->currentMode() == Inset::MATH_MODE @@ -1460,7 +1478,7 @@ bool Cursor::macroModeClose() posForward(); } else if (in && in->currentMode() == Inset::MATH_MODE && atom.nucleus()->currentMode() == Inset::TEXT_MODE - && name != from_ascii("text")) { + && !keep_mathmode) { MathAtom at = createInsetMath("text", buffer()); at.nucleus()->cell(0).push_back(atom); niceInsert(at); @@ -1586,7 +1604,7 @@ void Cursor::normalize() << pos() << ' ' << lastpos() << " in idx: " << idx() << " in atom: '"; odocstringstream os; - otexrowstream ots(os, false); + otexrowstream ots(os); WriteStream wi(ots, false, true, WriteStream::wsDefault); inset().asInsetMath()->write(wi); lyxerr << to_utf8(os.str()) << endl; @@ -1711,6 +1729,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()); @@ -1825,10 +1932,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. @@ -1952,7 +2062,7 @@ docstring parbreak(Cursor const * cur) return os.str(); } -} +} // namespace docstring Cursor::selectionAsString(bool with_label) const @@ -2004,23 +2114,16 @@ docstring Cursor::selectionAsString(bool with_label) const } -docstring Cursor::currentState() const +docstring Cursor::currentState(bool devel_mode) const { 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 + info(os, devel_mode); return os.str(); } if (inTexted()) - return text()->currentState(*this); + return text()->currentState(*this, devel_mode); return docstring(); } @@ -2036,10 +2139,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(); } @@ -2091,6 +2198,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... @@ -2138,6 +2246,7 @@ void Cursor::sanitize() { setBuffer(&bv_->buffer()); DocIterator::sanitize(); + new_word_.sanitize(); if (selection()) anchor_.sanitize(); else @@ -2331,4 +2440,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