X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FCursor.cpp;h=ac230a15e003cb2ca9ad18163e97f0e583bbce02;hb=09df753df4c24470617c64d25eae6df2db85dfed;hp=b3bdadeb6cf3cbe65ba4500a0e550481dfa16253;hpb=880b6c4d6ebd6f8e36b340a3efa3cb13108cc478;p=lyx.git diff --git a/src/Cursor.cpp b/src/Cursor.cpp index b3bdadeb6c..ac230a15e0 100644 --- a/src/Cursor.cpp +++ b/src/Cursor.cpp @@ -15,7 +15,6 @@ #include "Bidi.h" #include "BufferView.h" -#include "bufferview_funcs.h" #include "Buffer.h" #include "Cursor.h" #include "CoordCache.h" @@ -34,16 +33,16 @@ #include "Paragraph.h" #include "paragraph_funcs.h" #include "ParIterator.h" +#include "TextMetrics.h" #include "insets/InsetTabular.h" #include "insets/InsetText.h" -#include "mathed/MathData.h" #include "mathed/InsetMath.h" #include "mathed/InsetMathScript.h" #include "mathed/MacroTable.h" - -#include "support/limited_stack.h" +#include "mathed/MathData.h" +#include "mathed/MathMacro.h" #include #include @@ -53,14 +52,15 @@ #include #include -namespace lyx { - using std::string; using std::vector; using std::endl; using std::min; using std::for_each; + +namespace lyx { + namespace { bool @@ -96,9 +96,9 @@ namespace { int xo; int yo; Inset const * inset = &it.inset(); - std::map const & data = + std::map const & data = c.bv().coordCache().getInsets().getData(); - std::map::const_iterator I = data.find(inset); + std::map::const_iterator I = data.find(inset); // FIXME: in the case where the inset is not in the cache, this // means that no part of it is visible on screen. In this case @@ -109,7 +109,7 @@ namespace { return it; } - Point o = I->second; + Point o = I->second.pos; inset->cursorPos(c.bv(), it.top(), c.boundary(), xo, yo); // Convert to absolute xo += o.x_; @@ -131,6 +131,7 @@ namespace { } + /* /// moves position closest to (x, y) in given box bool bruteFind(Cursor & cursor, int x, int y, int xlow, int xhigh, int ylow, int yhigh) @@ -160,7 +161,7 @@ namespace { for ( ; it != et; it.forwardPos(true)) { // avoid invalid nesting when selecting if (!cursor.selection() || positionable(it, cursor.anchor_)) { - Point p = bv_funcs::getPos(bv, it, false); + Point p = bv.getPos(it, false); int xo = p.x_; int yo = p.y_; if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) { @@ -185,6 +186,7 @@ namespace { return false; } + */ /// moves position closest to (x, y) in given box @@ -205,7 +207,7 @@ namespace { // << " xlow: " << xlow << " xhigh: " << xhigh // << " ylow: " << ylow << " yhigh: " << yhigh // << endl; - Inset & inset = bv.buffer()->inset(); + Inset & inset = bv.buffer().inset(); DocIterator it = doc_iterator_begin(inset); it.pit() = from; DocIterator et = doc_iterator_end(inset); @@ -215,9 +217,9 @@ namespace { for ( ; it != et; it.forwardPos()) { // avoid invalid nesting when selecting - if (bv_funcs::status(&bv, it) == bv_funcs::CUR_INSIDE + if (bv.cursorStatus(it) == CUR_INSIDE && (!cur.selection() || positionable(it, cur.anchor_))) { - Point p = bv_funcs::getPos(bv, it, false); + Point p = bv.getPos(it, false); int xo = p.x_; int yo = p.y_; if (xlow <= xo && xo <= xhigh && ylow <= yo && yo <= yhigh) { @@ -252,8 +254,8 @@ namespace { odocstringstream ods; ods << '\n'; // only add blank line if we're not in an ERT or Listings inset - if (par.ownerCode() != Inset::ERT_CODE - && par.ownerCode() != Inset::LISTINGS_CODE) + if (par.ownerCode() != ERT_CODE + && par.ownerCode() != LISTINGS_CODE) ods << '\n'; return ods.str(); } @@ -265,7 +267,8 @@ namespace { // bv functions are not yet available! Cursor::Cursor(BufferView & bv) : DocIterator(), bv_(&bv), anchor_(), x_target_(-1), textTargetOffset_(0), - selection_(false), mark_(false), logicalpos_(false) + selection_(false), mark_(false), logicalpos_(false), + current_font(inherit_font) {} @@ -301,8 +304,7 @@ void Cursor::dispatch(FuncRequest const & cmd0) // store some values to be used inside of the handlers getPos(beforeDispX_, beforeDispY_); - beforeDispDepth_ = depth(); - + beforeDispatchCursor_ = *this; for (; depth(); pop()) { LYXERR(Debug::DEBUG) << "Cursor::dispatch: cmd: " << cmd0 << endl << *this << endl; @@ -319,6 +321,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()) { @@ -326,6 +329,10 @@ void Cursor::dispatch(FuncRequest const & cmd0) operator=(safe); disp_.update(Update::None); disp_.dispatched(false); + } else { + // restore the previous one because nested Cursor::dispatch calls + // are possible which would change it + beforeDispatchCursor_ = safe.beforeDispatchCursor_; } } @@ -346,8 +353,7 @@ BufferView & Cursor::bv() const Buffer & Cursor::buffer() const { BOOST_ASSERT(bv_); - BOOST_ASSERT(bv_->buffer()); - return *bv_->buffer(); + return bv_->buffer(); } @@ -413,7 +419,7 @@ int Cursor::currentMode() void Cursor::getPos(int & x, int & y) const { - Point p = bv_funcs::getPos(bv(), *this, boundary()); + Point p = bv().getPos(*this, boundary()); x = p.x_; y = p.y_; } @@ -507,9 +513,7 @@ void Cursor::setSelection() { selection() = true; // A selection with no contents is not a selection -#ifdef WITH_WARNINGS -#warning doesnt look ok -#endif + // FIXME: doesnt look ok if (pit() == anchor().pit() && pos() == anchor().pos()) selection() = false; } @@ -584,9 +588,11 @@ bool Cursor::selHandle(bool sel) if (sel == selection()) return false; + if (!sel) + cap::saveSelection(*this); + resetAnchor(); selection() = sel; - cap::saveSelection(*this); return true; } @@ -669,17 +675,17 @@ bool Cursor::openable(MathAtom const & t) const // we can't move into anything new during selection if (depth() >= anchor_.depth()) return false; - if (!ptr_cmp(t.nucleus(), &anchor_[depth()].inset())) + if (t.nucleus() != &anchor_[depth()].inset()) return false; return true; } -void Cursor::setScreenPos(int x, int y) +void Cursor::setScreenPos(int x, int /*y*/) { setTargetX(x); - bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight()); + //bruteFind(*this, x, y, 0, bv().workWidth(), 0, bv().workHeight()); } @@ -938,7 +944,13 @@ bool Cursor::macroModeClose() InsetMathNest * const in = inset().asInsetMath()->asNestInset(); if (in && in->interpretString(*this, s)) return true; - plainInsert(createInsetMath(name)); + MathAtom atom = createInsetMath(name); + MathMacro * atomAsMacro = atom.nucleus()->asMacro(); + if (atomAsMacro) { + // make non-greedy, i.e. don't eat parameters from the right + atomAsMacro->setDisplayMode(MathMacro::DISPLAY_NONGREEDY_INIT); + } + plainInsert(atom); return true; } @@ -1005,9 +1017,7 @@ InsetMathUnknown * Cursor::activeMacro() void Cursor::pullArg() { -#ifdef WITH_WARNINGS -#warning Look here -#endif + // FIXME: Look here MathData ar = cell(); if (popLeft() && inMathed()) { plainErase(); @@ -1021,9 +1031,7 @@ void Cursor::pullArg() void Cursor::touch() { -#ifdef WITH_WARNINGS -#warning look here -#endif + // FIXME: look here #if 0 DocIterator::const_iterator it = begin(); DocIterator::const_iterator et = end(); @@ -1076,7 +1084,7 @@ bool Cursor::upDownInMath(bool up) // 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 + // 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); @@ -1090,12 +1098,12 @@ bool Cursor::upDownInMath(bool up) right = pos() == textRow().endpos(); } if ((!left && !right) || - (left && !right && xo < x_target_) || + (left && !right && xo < x_target_) || (!left && right && x_target_ < xo)) setTargetX(xo); else xo = targetX(); - } else + } else xo = targetX(); // try neigbouring script insets @@ -1168,7 +1176,7 @@ bool Cursor::upDownInMath(bool up) } -bool Cursor::upDownInText(bool up) +bool Cursor::upDownInText(bool up, bool & updateNeeded) { BOOST_ASSERT(text()); @@ -1183,13 +1191,14 @@ bool Cursor::upDownInText(bool up) // if we cannot move up/down inside this inset anymore if (x_target_ == -1) setTargetX(xo); - else if (xo - textTargetOffset() != x_target() && depth() == beforeDispDepth_) { + 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 + // 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); @@ -1203,16 +1212,16 @@ bool Cursor::upDownInText(bool up) right = pos() == textRow().endpos(); } if ((!left && !right) || - (left && !right && xo < x_target_) || + (left && !right && xo < x_target_) || (!left && right && x_target_ < xo)) setTargetX(xo); else xo = targetX(); - } else + } else xo = targetX(); // first get the current line - TextMetrics const & tm = bv_->textMetrics(text()); + TextMetrics & tm = bv_->textMetrics(text()); ParagraphMetrics const & pm = tm.parMetrics(pit()); int row; if (pos() && boundary()) @@ -1225,20 +1234,20 @@ bool Cursor::upDownInText(bool up) if (pit() == 0 && row == 0) return false; } else { - if (pit() + 1 >= int(text()->paragraphs().size()) && + if (pit() + 1 >= int(text()->paragraphs().size()) && row + 1 >= int(pm.rows().size())) return false; } - + // with and without selection are handled differently if (!selection()) { - int yo = bv_funcs::getPos(bv(), *this, boundary()).y_; + int yo = bv().getPos(*this, boundary()).y_; Cursor old = *this; // To next/previous row if (up) - text()->editXY(*this, xo, yo - textRow().ascent() - 1); + tm.editXY(*this, xo, yo - textRow().ascent() - 1); else - text()->editXY(*this, xo, yo + textRow().descent() + 1); + tm.editXY(*this, xo, yo + textRow().descent() + 1); clearSelection(); // This happens when you move out of an inset. @@ -1248,12 +1257,11 @@ bool Cursor::upDownInText(bool up) Cursor dummy = *this; if (dummy == old) ++dummy.pos(); - - bool const changed = bv().checkDepm(dummy, old); - - // Make sure that cur gets back whatever happened to dummy(Lgb) - if (changed) + if (bv().checkDepm(dummy, old)) { + updateNeeded = true; + // Make sure that cur gets back whatever happened to dummy(Lgb) operator=(dummy); + } } else { // if there is a selection, we stay out of any inset, and just jump to the right position: Cursor old = *this; @@ -1273,10 +1281,10 @@ bool Cursor::upDownInText(bool up) top().pos() = std::min(tm.x2pos(pit(), 0, xo), top().lastpos()); } } - - bv().checkDepm(*this, old); + + updateNeeded |= bv().checkDepm(*this, old); } - + updateTextTargetOffset(); return true; } @@ -1335,7 +1343,7 @@ docstring Cursor::selectionAsString(bool label) const return docstring(); if (inTexted()) { - Buffer const & buffer = *bv().buffer(); + Buffer const & buffer = bv().buffer(); ParagraphList const & pars = text()->paragraphs(); // should be const ... @@ -1397,8 +1405,6 @@ Encoding const * Cursor::getEncoding() const { if (empty()) return 0; - if (!bv().buffer()) - return 0; int s = 0; // go up until first non-0 text is hit // (innermost text is 0 in mathed) @@ -1408,7 +1414,7 @@ Encoding const * Cursor::getEncoding() const CursorSlice const & sl = operator[](s); Text const & text = *sl.text(); Font font = text.getPar(sl.pit()).getFont( - bv().buffer()->params(), sl.pos(), outerFont(sl.pit(), text.paragraphs())); + bv().buffer().params(), sl.pos(), outerFont(sl.pit(), text.paragraphs())); return font.language()->encoding(); } @@ -1439,31 +1445,191 @@ void Cursor::noUpdate() 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. + // HACK. far from being perfect... - int s = 0; // go up until first non-0 text is hit // (innermost text is 0 in mathed) + int s = 0; for (s = depth() - 1; s >= 0; --s) if (operator[](s).text()) break; CursorSlice const & sl = operator[](s); Text const & text = *sl.text(); - Font font = text.getPar(sl.pit()).getFont( - bv().buffer()->params(), - sl.pos(), + 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) + && !tm.isRTLBoundary(sl.pit(), pos))) + --pos; + } + + // get font at the position + Font font = par.getFont(bv().buffer().params(), pos, outerFont(sl.pit(), text.paragraphs())); return font; } -void Cursor::fixIfBroken() +bool Cursor::fixIfBroken() { if (DocIterator::fixIfBroken()) { clearSelection(); resetAnchor(); + return true; + } + return false; +} + + +bool notifyCursorLeaves(DocIterator const & old, Cursor & cur) +{ + // find inset in common + size_type i; + for (i = 0; i < old.depth() && i < cur.depth(); ++i) { + if (&old.inset() != &cur.inset()) + break; + } + + // notify everything on top of the common part in old cursor, + // but stop if the inset claims the cursor to be invalid now + for (; i < old.depth(); ++i) { + if (old[i].inset().notifyCursorLeaves(cur)) + return true; + } + + return false; +} + + +void Cursor::setCurrentFont() +{ + CursorSlice const & cs = innerTextSlice(); + Paragraph const & par = cs.paragraph(); + pos_type cpit = cs.pit(); + pos_type cpos = cs.pos(); + Text const & ctext = *cs.text(); + TextMetrics const & tm = bv().textMetrics(&ctext); + + // are we behind previous char in fact? -> go to that char + if (cpos > 0 && boundary()) + --cpos; + + // find position to take the font from + if (cpos != 0) { + // paragraph end? -> font of last char + if (cpos == lastpos()) + --cpos; + // on space? -> look at the words in front of space + else if (cpos > 0 && par.isSeparator(cpos)) { + // abc| def -> font of c + // abc |[WERBEH], i.e. boundary==true -> font of c + // abc [WERBEH]| def, font of the space + if (!tm.isRTLBoundary(cpit, cpos)) + --cpos; + } + } + + // get font + BufferParams const & bufparams = buffer().params(); + current_font = par.getFontSettings(bufparams, cpos); + real_current_font = tm.getDisplayFont(cpit, cpos); + + // special case for paragraph end + if (cs.pos() == lastpos() + && tm.isRTLBoundary(cpit, cs.pos()) + && !boundary()) { + Language const * lang = par.getParLanguage(bufparams); + current_font.setLanguage(lang); + current_font.fontInfo().setNumber(FONT_OFF); + real_current_font.setLanguage(lang); + real_current_font.fontInfo().setNumber(FONT_OFF); } } +bool Cursor::textUndo() +{ + DocIterator dit = *this; + // Undo::textUndo() will modify dit. + if (!bv_->buffer().undo().textUndo(dit)) + return false; + // Set cursor + setCursor(dit); + selection() = false; + resetAnchor(); + fixIfBroken(); + return true; +} + + +bool Cursor::textRedo() +{ + DocIterator dit = *this; + // Undo::textRedo() will modify dit. + if (!bv_->buffer().undo().textRedo(dit)) + return false; + // Set cursor + setCursor(dit); + selection() = false; + resetAnchor(); + fixIfBroken(); + return true; +} + + +void Cursor::finishUndo() +{ + bv_->buffer().undo().finishUndo(); +} + + +void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to) +{ + bv_->buffer().undo().recordUndo(*this, kind, from, to); +} + + +void Cursor::recordUndo(UndoKind kind, pit_type from) +{ + bv_->buffer().undo().recordUndo(*this, kind, from); +} + + +void Cursor::recordUndo(UndoKind kind) +{ + bv_->buffer().undo().recordUndo(*this, kind); +} + + +void Cursor::recordUndoInset(UndoKind kind) +{ + bv_->buffer().undo().recordUndoInset(*this, kind); +} + + +void Cursor::recordUndoFullDocument() +{ + bv_->buffer().undo().recordUndoFullDocument(*this); +} + + +void Cursor::recordUndoSelection() +{ + bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO, + selBegin().pit(), selEnd().pit()); +} + + } // namespace lyx