X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fundo.C;h=33b97827325f006009e10dbcc3ef59165aa0c583;hb=52eb91c94fb70d58dceef430659c8781de2eccda;hp=634827a10b3723c30c2fc38196fc58a1c356e6c7;hpb=5c2f0c5c4642d9302a9bb7d19327a7dc4539f079;p=lyx.git diff --git a/src/undo.C b/src/undo.C index 634827a10b..33b9782732 100644 --- a/src/undo.C +++ b/src/undo.C @@ -17,286 +17,283 @@ #include "undo.h" #include "buffer.h" +#include "buffer_funcs.h" +#include "cursor.h" #include "debug.h" #include "BufferView.h" -#include "funcrequest.h" -#include "iterators.h" #include "lyxtext.h" #include "paragraph.h" +#include "ParagraphList.h" -#include "insets/updatableinset.h" -#include "insets/insettext.h" +#include "mathed/MathSupport.h" +#include "insets/inset.h" -#include +#include + +namespace lyx { + +using std::advance; using std::endl; -using lyx::paroffset_type; +namespace { + /// The flag used by finishUndo(). bool undo_finished; -/// Whether actions are not added to the undo stacks. -bool undo_frozen; - -Undo::Undo(undo_kind kind_arg, int text_arg, - int first, int last, - int cursor, int cursor_pos_arg, - ParagraphList const & par) - : - kind(kind_arg), - text(text_arg), - first_par_offset(first), - last_par_offset(last), - cursor_par_offset(cursor), - cursor_pos(cursor_pos_arg), - pars(par) -{} - std::ostream & operator<<(std::ostream & os, Undo const & undo) { - return os << " text: " << undo.text - << " first: " << undo.first_par_offset - << " last: " << undo.last_par_offset - << " cursor: " << undo.cursor_par_offset - << "/" << undo.cursor_pos; + return os << " from: " << undo.from << " end: " << undo.end + << " cell:\n" << undo.cell + << " cursor:\n" << undo.cursor; } -namespace { +bool samePar(StableDocIterator const & i1, StableDocIterator const & i2) +{ + StableDocIterator tmpi2 = i2; + tmpi2.pos() = i1.pos(); + return i1 == tmpi2; +} -void recordUndo(Undo::undo_kind kind, - LyXText * text, paroffset_type firstpar, paroffset_type lastpar, + +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 = text->bv()->buffer(); - - ParagraphList & plist = text->ownerParagraphs(); - ParagraphList::iterator first = plist.begin(); - advance(first, firstpar); - ParagraphList::iterator last = plist.begin(); - advance(last, lastpar); - - // try to find the appropriate list by counting the - // texts from buffer begin - ParIterator null = buf->par_iterator_end(); - - int tcount = 0; - // it.text() returns 0 for outermost text. - if (text != text->bv()->text) - for (ParIterator it = buf->par_iterator_begin(); it != null; ++it, ++tcount) - if (it.text() == text) - break; - - // and calculate a stable reference to them - int const first_offset = firstpar; - int const last_offset = plist.size() - lastpar; + 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; // 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_offset == first_offset - && buf->undostack().top().last_par_offset == last_offset) { - // No additonal undo recording needed - - // effectively, we combine undo recordings to one. - return; - } + // 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; + + // 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); } - // Record the cursor position in a stable way. - int const cursor_offset = text->cursor.par(); + // push the undo entry to undo stack + //lyxerr << "undo record: " << stack.top() << std::endl; + stack.push(undo); - // make and push the Undo entry - stack.push(Undo(kind, tcount, - first_offset, last_offset, - cursor_offset, text->cursor.pos(), - ParagraphList())); - lyxerr << "undo record: " << stack.top() << endl; + // next time we'll try again to combine entries if possible + undo_finished = false; +} - // record the relevant paragraphs - ParagraphList & undo_pars = stack.top().pars; - for (ParagraphList::iterator it = first; it != last; ++it) { - undo_pars.push_back(*it); - undo_pars.back().id(it->id()); - } - undo_pars.push_back(*last); - undo_pars.back().id(last->id()); +void recordUndo(Undo::undo_kind kind, + LCursor & cur, pit_type first_pit, pit_type last_pit, + limited_stack & stack) +{ + BOOST_ASSERT(first_pit <= cur.lastpit()); + BOOST_ASSERT(last_pit <= cur.lastpit()); - // 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) +bool textUndoOrRedo(BufferView & bv, + limited_stack & stack, limited_stack & otherstack) { - lyxerr << "undo, performing: " << undo << endl; - Buffer * buf = bv->buffer(); - ParIterator plit = buf->par_iterator_begin(); - ParIterator null = buf->par_iterator_end(); - - int tcount = undo.text; - for ( ; tcount && plit != null; ++plit, --tcount) - ; - - LyXText * text = plit.text(); - if (!text) - text = bv->text; - ParagraphList & plist = text->ownerParagraphs(); + finishUndo(); - // remove new stuff between first and last - { - ParagraphList::iterator first = plist.begin(); - advance(first, undo.first_par_offset); - ParagraphList::iterator last = plist.begin(); - advance(last, plist.size() - undo.last_par_offset); - 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_offset); + 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: " << text << " inset: " << plit.inset() << endl; - InsetOld * inset = plit.inset(); - if (inset) { - // Magic needed to cope with inset locking - FuncRequest cmd(bv, LFUN_INSET_EDIT, "left"); - inset->localDispatch(cmd); - } - - // set cursor again to force the position to be the right one - text->setCursorIntern(undo.cursor_par_offset, undo.cursor_pos); - - // clear any selection - text->clearSelection(); - text->selection.cursor = text->cursor; - text->updateCounters(); - - // rebreak the entire document - bv->text->fullRebreak(); - + // 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()) { - /* - * Finish the undo operation in the case there was no entry - * on the stack to perform. - */ - 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(); - Buffer * buf = bv->buffer(); - ParagraphList & plist = buf->paragraphs(); - if (undo.first_par_offset + undo.last_par_offset <= int(plist.size())) { - ParagraphList::iterator first = plist.begin(); - advance(first, undo.first_par_offset); - ParagraphList::iterator last = plist.begin(); - advance(last, plist.size() - undo.last_par_offset + 1); - otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last); - } - otherstack.top().cursor_pos = text - lyxerr << " undo other: " << otherstack.top() << 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) { - 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