X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fundo.C;h=33b97827325f006009e10dbcc3ef59165aa0c583;hb=969ab85d985485f503790cb13f98a582d4e1cdb5;hp=44524212f55873b0926080c91c6346cac4670119;hpb=c38370d1c3dc594b17c53c28582f8092a15901d9;p=lyx.git diff --git a/src/undo.C b/src/undo.C index 44524212f5..33b9782732 100644 --- a/src/undo.C +++ b/src/undo.C @@ -17,302 +17,283 @@ #include "undo.h" #include "buffer.h" +#include "buffer_funcs.h" +#include "cursor.h" #include "debug.h" #include "BufferView.h" -#include "iterators.h" #include "lyxtext.h" #include "paragraph.h" +#include "ParagraphList.h" -#include "insets/updatableinset.h" // for dynamic_cast +#include "mathed/MathSupport.h" +#include "insets/inset.h" -using lyx::paroffset_type; +#include -/// The flag used by finishUndo(). -bool undo_finished; - -/// Whether actions are not added to the undo stacks. -bool undo_frozen; +namespace lyx { -Undo::Undo(undo_kind kind_, int text_, int index_, - int first_par_, int end_par_, int cursor_par_, int cursor_pos_) - : - kind(kind_), - text(text_), - index(index_), - first_par(first_par_), - end_par(end_par_), - cursor_par(cursor_par_), - cursor_pos(cursor_pos_) -{} +using std::advance; +using std::endl; namespace { +/// The flag used by finishUndo(). +bool undo_finished; + + std::ostream & operator<<(std::ostream & os, Undo const & undo) { - return os << " text: " << undo.text - << " index: " << undo.index - << " first: " << undo.first_par - << " from end: " << undo.end_par - << " cursor: " << undo.cursor_par - << "/" << undo.cursor_pos; + return os << " from: " << undo.from << " end: " << undo.end + << " cell:\n" << undo.cell + << " cursor:\n" << undo.cursor; } -// translates LyXText pointer into offset count from document begin -ParIterator text2pit(LyXText * text, int & tcount) +bool samePar(StableDocIterator const & i1, StableDocIterator const & i2) { - tcount = 0; - Buffer * buf = text->bv()->buffer(); - ParIterator pit = buf->par_iterator_begin(); - ParIterator end = buf->par_iterator_end(); - - // it.text() returns 0 for outermost text. - if (text == text->bv()->text) - return pit; - - for ( ; pit != end; ++pit, ++tcount) - if (pit.text() == text) - return pit; - lyxerr << "undo: should not happen" << std::endl; - return end; + StableDocIterator tmpi2 = i2; + tmpi2.pos() = i1.pos(); + return i1 == tmpi2; } -// translates offset from buffer begin to ParIterator -ParIterator num2pit(BufferView * bv, int num) +void doRecordUndo(Undo::undo_kind kind, + DocIterator const & cell, + pit_type first_pit, pit_type last_pit, + DocIterator const & cur, + BufferParams const & bparams, + bool isFullBuffer, + limited_stack & stack) { - Buffer * buf = bv->buffer(); - ParIterator pit = buf->par_iterator_begin(); - ParIterator end = buf->par_iterator_end(); - - for ( ; num && pit != end; ++pit, --num) - ; + if (first_pit > last_pit) + std::swap(first_pit, last_pit); + // create the position information of the Undo entry + Undo undo; + undo.kind = kind; + undo.cell = cell; + undo.cursor = cur; + undo.bparams = bparams ; + undo.isFullBuffer = isFullBuffer; + //lyxerr << "recordUndo: cur: " << cur << endl; + //lyxerr << "recordUndo: pos: " << cur.pos() << endl; + //lyxerr << "recordUndo: cell: " << cell << endl; + undo.from = first_pit; + undo.end = cell.lastpit() - last_pit; - if (pit != end) - return pit; + // Undo::ATOMIC are always recorded (no overlapping there). + // As nobody wants all removed character appear one by one when undoing, + // we want combine 'similar' non-ATOMIC undo recordings to one. + if (!undo_finished + && kind != Undo::ATOMIC + && !stack.empty() + && samePar(stack.top().cell, undo.cell) + && stack.top().kind == undo.kind + && stack.top().from == undo.from + && stack.top().end == undo.end) + return; - // don't crash early... - lyxerr << "undo: num2pit: num: " << num << std::endl; - BOOST_ASSERT(false); - return buf->par_iterator_begin(); -} + // fill in the real data to be saved + if (cell.inMathed()) { + // simply use the whole cell + undo.array = asString(cell.cell()); + } else { + // some more effort needed here as 'the whole cell' of the + // main LyXText _is_ the whole document. + // record the relevant paragraphs + LyXText const * text = cell.text(); + BOOST_ASSERT(text); + ParagraphList const & plist = text->paragraphs(); + ParagraphList::const_iterator first = plist.begin(); + advance(first, first_pit); + ParagraphList::const_iterator last = plist.begin(); + advance(last, last_pit + 1); + undo.pars = ParagraphList(first, last); + } + // push the undo entry to undo stack + //lyxerr << "undo record: " << stack.top() << std::endl; + stack.push(undo); -// translates offset from buffer begin to LyXText -LyXText * pit2text(BufferView * bv, ParIterator const & pit) -{ - LyXText * text = pit.text(); - return text ? text : bv->text; + // next time we'll try again to combine entries if possible + undo_finished = false; } void recordUndo(Undo::undo_kind kind, - LyXText * text, paroffset_type first_par, paroffset_type last_par, + LCursor & cur, pit_type first_pit, pit_type last_pit, limited_stack & stack) { - Buffer * buf = text->bv()->buffer(); + BOOST_ASSERT(first_pit <= cur.lastpit()); + BOOST_ASSERT(last_pit <= cur.lastpit()); - int const end_par = text->ownerParagraphs().size() - last_par; - - // Undo::ATOMIC are always recorded (no overlapping there). - // overlapping only with insert and delete inside one paragraph: - // nobody wants all removed character appear one by one when undoing. - if (!undo_finished && kind != Undo::ATOMIC) { - // Check whether storing is needed. - if (!buf->undostack().empty() - && buf->undostack().top().kind == kind - && buf->undostack().top().first_par == first_par - && buf->undostack().top().end_par == end_par) { - // No additonal undo recording needed - - // effectively, we combine undo recordings to one. - return; - } - } - - // make and push the Undo entry - int textnum; - ParIterator pit = text2pit(text, textnum); - stack.push(Undo(kind, textnum, pit.index(), - first_par, end_par, text->cursor.par(), text->cursor.pos())); - lyxerr << "undo record: " << stack.top() << std::endl; - - // record the relevant paragraphs - ParagraphList & undo_pars = stack.top().pars; - - ParagraphList & plist = text->ownerParagraphs(); - ParagraphList::iterator first = plist.begin(); - advance(first, first_par); - ParagraphList::iterator last = plist.begin(); - advance(last, last_par); - - for (ParagraphList::iterator it = first; it != last; ++it) - undo_pars.push_back(*it); - undo_pars.push_back(*last); - - // and make sure that next time, we should be combining if possible - undo_finished = false; + doRecordUndo(kind, cur, first_pit, last_pit, cur, + cur.bv().buffer()->params(), false, stack); } -// returns false if no undo possible -bool performUndoOrRedo(BufferView * bv, Undo const & undo) + +// Returns false if no undo possible. +bool textUndoOrRedo(BufferView & bv, + limited_stack & stack, limited_stack & otherstack) { - lyxerr << "undo, performing: " << undo << std::endl; - ParIterator pit = num2pit(bv, undo.text); - LyXText * text = pit2text(bv, pit); - ParagraphList & plist = text->ownerParagraphs(); + finishUndo(); - // remove new stuff between first and last - { - ParagraphList::iterator first = plist.begin(); - advance(first, undo.first_par); - ParagraphList::iterator last = plist.begin(); - advance(last, plist.size() - undo.end_par); - plist.erase(first, ++last); + if (stack.empty()) { + // Nothing to do. + return false; } - // re-insert old stuff instead - if (plist.empty()) { - plist.assign(undo.pars.begin(), undo.pars.end()); + // Adjust undo stack and get hold of current undo data. + Undo undo = stack.top(); + stack.pop(); + + // We will store in otherstack the part of the document under 'undo' + Buffer * buf = bv.buffer(); + DocIterator cell_dit = undo.cell.asDocIterator(&buf->inset()); + + doRecordUndo(Undo::ATOMIC, cell_dit, + undo.from, cell_dit.lastpit() - undo.end, bv.cursor(), + undo.bparams, undo.isFullBuffer, + otherstack); + + // This does the actual undo/redo. + //lyxerr << "undo, performing: " << undo << std::endl; + DocIterator dit = undo.cell.asDocIterator(&buf->inset()); + if (undo.isFullBuffer) { + // This is a full document + otherstack.top().bparams = buf->params(); + buf->params() = undo.bparams; + buf->paragraphs() = undo.pars; + } else if (dit.inMathed()) { + // We stored the full cell here as there is not much to be + // gained by storing just 'a few' paragraphs (most if not + // all math inset cells have just one paragraph!) + // lyxerr << "undo.array=" << to_ascii(undo.array) <paragraphs(); + + // remove new stuff between first and last ParagraphList::iterator first = plist.begin(); - advance(first, undo.first_par); + advance(first, undo.from); + ParagraphList::iterator last = plist.begin(); + advance(last, plist.size() - undo.end); + plist.erase(first, last); + + // re-insert old stuff instead + first = plist.begin(); + advance(first, undo.from); + + // this ugly stuff is needed until we get rid of the + // inset_owner backpointer + ParagraphList::iterator pit = undo.pars.begin(); + ParagraphList::iterator const end = undo.pars.end(); + for (; pit != end; ++pit) + pit->setInsetOwner(dit.realInset()); plist.insert(first, undo.pars.begin(), undo.pars.end()); + updateLabels(*buf); } - // set cursor - lyxerr << "undo, text: " << undo.text - << " inset: " << pit.inset() - << " index: " << undo.index - << std::endl; - - // set cursor again to force the position to be the right one - text->cursor.par(undo.cursor_par); - text->cursor.pos(undo.cursor_pos); - - // clear any selection - text->clearSelection(); - text->selection.cursor = text->cursor; - text->updateCounters(); - - // rebreak the entire lyxtext - bv->text->fullRebreak(); - - InsetOld * inset = pit.inset(); - if (inset) { - // magic needed to cope with inset locking - bv->lockInset(dynamic_cast(inset)); - } - + // Set cursor + LCursor & cur = bv.cursor(); + cur.setCursor(undo.cursor.asDocIterator(&buf->inset())); + cur.selection() = false; + cur.resetAnchor(); finishUndo(); + return true; } +} // namespace anon -// returns false if no undo possible -bool textUndoOrRedo(BufferView * bv, - limited_stack & stack, limited_stack & otherstack) -{ - if (stack.empty()) { - // nothing to do - freezeUndo(); - bv->unlockInset(bv->theLockingInset()); - finishUndo(); - unFreezeUndo(); - return false; - } - Undo undo = stack.top(); - stack.pop(); - finishUndo(); +void finishUndo() +{ + // Make sure the next operation will be stored. + undo_finished = true; +} - if (!undo_frozen) { - otherstack.push(undo); - otherstack.top().pars.clear(); - ParIterator pit = num2pit(bv, undo.text); - ParagraphList & plist = pit.plist(); - if (undo.first_par + undo.end_par <= int(plist.size())) { - ParagraphList::iterator first = plist.begin(); - advance(first, undo.first_par); - ParagraphList::iterator last = plist.begin(); - advance(last, plist.size() - undo.end_par + 1); - otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last); - } - LyXText * text = pit2text(bv, pit); - otherstack.top().cursor_pos = text->cursor.pos(); - otherstack.top().cursor_par = text->cursor.par(); - lyxerr << " undo other: " << otherstack.top() << std::endl; - } - // Now we can unlock the inset for safety because the inset - // pointer could be changed during the undo-function. Anyway - // if needed we have to lock the right inset/position if this - // is requested. - freezeUndo(); - bv->unlockInset(bv->theLockingInset()); - bool const ret = performUndoOrRedo(bv, undo); - unFreezeUndo(); - return ret; +bool textUndo(BufferView & bv) +{ + return textUndoOrRedo(bv, bv.buffer()->undostack(), + bv.buffer()->redostack()); } -} // namespace anon - -void freezeUndo() +bool textRedo(BufferView & bv) { - // this is dangerous and for internal use only - undo_frozen = true; + return textUndoOrRedo(bv, bv.buffer()->redostack(), + bv.buffer()->undostack()); } -void unFreezeUndo() +void recordUndo(Undo::undo_kind kind, + LCursor & cur, pit_type first, pit_type last) { - // this is dangerous and for internal use only - undo_frozen = false; + Buffer * buf = cur.bv().buffer(); + recordUndo(kind, cur, first, last, buf->undostack()); + buf->redostack().clear(); + //lyxerr << "undostack:\n"; + //for (size_t i = 0, n = buf->undostack().size(); i != n && i < 6; ++i) + // lyxerr << " " << i << ": " << buf->undostack()[i] << std::endl; } -void finishUndo() +void recordUndo(LCursor & cur, Undo::undo_kind kind) { - // makes sure the next operation will be stored - undo_finished = true; + recordUndo(kind, cur, cur.pit(), cur.pit()); } -bool textUndo(BufferView * bv) +void recordUndoInset(LCursor & cur, Undo::undo_kind kind) { - return textUndoOrRedo(bv, bv->buffer()->undostack(), - bv->buffer()->redostack()); + LCursor c = cur; + c.pop(); + Buffer * buf = cur.bv().buffer(); + doRecordUndo(kind, c, c.pit(), c.pit(), cur, + buf->params(), false, buf->undostack()); } -bool textRedo(BufferView * bv) +void recordUndoSelection(LCursor & cur, Undo::undo_kind kind) { - return textUndoOrRedo(bv, bv->buffer()->redostack(), - bv->buffer()->undostack()); + recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit()); } -void recordUndo(Undo::undo_kind kind, - LyXText const * text, paroffset_type first, paroffset_type last) +void recordUndo(LCursor & cur, Undo::undo_kind kind, pit_type from) { - if (undo_frozen) - return; - Buffer * buf = text->bv()->buffer(); - recordUndo(kind, const_cast(text), first, last, buf->undostack()); - buf->redostack().clear(); + recordUndo(kind, cur, cur.pit(), from); } -void recordUndo(Undo::undo_kind kind, LyXText const * text, paroffset_type par) +void recordUndo(LCursor & cur, Undo::undo_kind kind, + pit_type from, pit_type to) { - recordUndo(kind, text, par, par); + recordUndo(kind, cur, from, to); } -void recordUndo(BufferView * bv, Undo::undo_kind kind) +void recordUndoFullDocument(BufferView * bv) { - recordUndo(kind, bv->text, bv->text->cursor.par()); + Buffer * buf = bv->buffer(); + doRecordUndo( + Undo::ATOMIC, + doc_iterator_begin(buf->inset()), + 0, buf->paragraphs().size() - 1, + bv->cursor(), + buf->params(), + true, + buf->undostack() + ); + undo_finished = false; } + + +} // namespace lyx