]> git.lyx.org Git - lyx.git/blobdiff - src/undo.C
code cosmetics to the iterator fix
[lyx.git] / src / undo.C
index d8af9e1a278e40cd3060e75d1d7cf8c36728dcd2..8e5fcc334f9acb23e249bba86f12a1268faf09db 100644 (file)
 #include "undo.h"
 
 #include "buffer.h"
+#include "cursor.h"
 #include "debug.h"
 #include "BufferView.h"
-#include "funcrequest.h"
-#include "iterators.h"
 #include "lyxtext.h"
 #include "paragraph.h"
 
-#include "insets/updatableinset.h"
-#include "insets/insettext.h"
+#include "mathed/math_support.h"
+#include "insets/inset.h"
 
-#include <iostream>
+#include <algorithm>
 
+using lyx::pit_type;
+
+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<Undo> & 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<Undo> & 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<Undo> & stack, limited_stack<Undo> & 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!)
+               asArray(undo.array, dit.cell());
        } else {
+               // Some finer machinery is needed here.
+               LyXText * text = dit.text();
+               BOOST_ASSERT(text);
+               ParagraphList & plist = text->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());
        }
 
-       // 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<Undo> & stack, limited_stack<Undo> & 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();
+       recordUndo(c, kind);
 }
 
 
-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<LyXText *>(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;
 }