X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fundo.C;h=33b97827325f006009e10dbcc3ef59165aa0c583;hb=969ab85d985485f503790cb13f98a582d4e1cdb5;hp=22b318117661d8bf2891ab120a8bd907302daa4f;hpb=8dccec5c46a1689fe3ea37144252e952771e1e1c;p=lyx.git diff --git a/src/undo.C b/src/undo.C index 22b3181176..33b9782732 100644 --- a/src/undo.C +++ b/src/undo.C @@ -1,102 +1,299 @@ -/* This file is part of - * ====================================================== - * - * LyX, The Document Processor - * - * Copyright 1995 Matthias Ettrich - * Copyright 1995-1999 The LyX Team. +/** + * \file undo.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. * - * ====================================================== */ + * \author Asger Alstrup + * \author Lars Gullik Bjønnes + * \author John Levon + * \author André Pönitz + * \author Jürgen Vigna + * + * Full author contact details are available in file CREDITS. + */ #include #include "undo.h" -#ifdef __GNUG__ -#pragma implementation -#endif +#include "buffer.h" +#include "buffer_funcs.h" +#include "cursor.h" +#include "debug.h" +#include "BufferView.h" +#include "lyxtext.h" +#include "paragraph.h" +#include "ParagraphList.h" + +#include "mathed/MathSupport.h" +#include "insets/inset.h" + +#include + + +namespace lyx { + +using std::advance; +using std::endl; + +namespace { -Undo::Undo(undo_kind kind_arg, - int number_before_arg, int number_behind_arg, - int cursor_par_arg, int cursor_pos_arg, - LyXParagraph * par_arg) +/// The flag used by finishUndo(). +bool undo_finished; + + +std::ostream & operator<<(std::ostream & os, Undo const & undo) { - kind = kind_arg; - number_of_before_par = number_before_arg; - number_of_behind_par = number_behind_arg; - number_of_cursor_par = cursor_par_arg; - cursor_pos = cursor_pos_arg; - par = par_arg; + return os << " from: " << undo.from << " end: " << undo.end + << " cell:\n" << undo.cell + << " cursor:\n" << undo.cursor; } -Undo::~Undo() +bool samePar(StableDocIterator const & i1, StableDocIterator const & i2) { - LyXParagraph * tmppar; - while (par) { - tmppar = par; - par = par->next; - delete tmppar; + StableDocIterator tmpi2 = i2; + tmpi2.pos() = i1.pos(); + return i1 == tmpi2; +} + + +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) +{ + 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). + // 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); } + + // push the undo entry to undo stack + //lyxerr << "undo record: " << stack.top() << std::endl; + stack.push(undo); + + // next time we'll try again to combine entries if possible + undo_finished = false; } -UndoStack::UndoStack() - : limit(100) {} +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()); + + doRecordUndo(kind, cur, first_pit, last_pit, cur, + cur.bv().buffer()->params(), false, stack); +} + -Undo * UndoStack::pop() +// Returns false if no undo possible. +bool textUndoOrRedo(BufferView & bv, + limited_stack & stack, limited_stack & otherstack) { - if (stakk.empty()) return 0; - Undo * result = stakk.front(); - stakk.pop_front(); - return result; + finishUndo(); + + if (stack.empty()) { + // Nothing to do. + return false; + } + + // 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.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 + LCursor & cur = bv.cursor(); + cur.setCursor(undo.cursor.asDocIterator(&buf->inset())); + cur.selection() = false; + cur.resetAnchor(); + finishUndo(); + + return true; } +} // namespace anon + -Undo * UndoStack::top() +void finishUndo() { - if (stakk.empty()) return 0; - return stakk.front(); + // Make sure the next operation will be stored. + undo_finished = true; } -UndoStack::~UndoStack() +bool textUndo(BufferView & bv) { - clear(); + return textUndoOrRedo(bv, bv.buffer()->undostack(), + bv.buffer()->redostack()); } -void UndoStack::clear() +bool textRedo(BufferView & bv) { - while (!stakk.empty()) { - Undo * tmp = stakk.front(); - stakk.pop_front(); - delete tmp; - } + return textUndoOrRedo(bv, bv.buffer()->redostack(), + bv.buffer()->undostack()); } -void UndoStack::SetStackLimit(Stakk::size_type l) +void recordUndo(Undo::undo_kind kind, + LCursor & cur, pit_type first, pit_type last) { - limit = l; + 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 UndoStack::push(Undo * undo_arg) +void recordUndo(LCursor & cur, Undo::undo_kind kind) { - if (!undo_arg) return; - - stakk.push_front(undo_arg); - if (stakk.size() > limit) { - Undo * tmp = stakk.back(); - stakk.pop_back(); - delete tmp; - } + recordUndo(kind, cur, cur.pit(), cur.pit()); } -bool UndoStack::empty() const { - return stakk.empty(); +void recordUndoInset(LCursor & cur, Undo::undo_kind kind) +{ + LCursor c = cur; + c.pop(); + Buffer * buf = cur.bv().buffer(); + doRecordUndo(kind, c, c.pit(), c.pit(), cur, + buf->params(), false, buf->undostack()); } + + +void recordUndoSelection(LCursor & cur, Undo::undo_kind kind) +{ + recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit()); +} + + +void recordUndo(LCursor & cur, Undo::undo_kind kind, pit_type from) +{ + recordUndo(kind, cur, cur.pit(), from); +} + + +void recordUndo(LCursor & cur, Undo::undo_kind kind, + pit_type from, pit_type to) +{ + recordUndo(kind, cur, from, to); +} + + +void recordUndoFullDocument(BufferView * bv) +{ + 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