* \author John Levon
* \author André Pönitz
* \author Jürgen Vigna
+ * \author Abdelrazak Younes
*
* Full author contact details are available in file CREDITS.
*/
#include "Undo.h"
#include "Buffer.h"
+#include "BufferParams.h"
#include "buffer_funcs.h"
-#include "LCursor.h"
-#include "debug.h"
-#include "BufferView.h"
-#include "LyXText.h"
+#include "DocIterator.h"
#include "Paragraph.h"
#include "ParagraphList.h"
+#include "Text.h"
#include "mathed/MathSupport.h"
-#include "mathed/MathArray.h"
+#include "mathed/MathData.h"
#include "insets/Inset.h"
+#include "support/debug.h"
+#include "support/limited_stack.h"
+
#include <algorithm>
+#include <ostream>
+using namespace std;
+using namespace lyx::support;
namespace lyx {
-using std::advance;
-using std::endl;
+
+/**
+These are the elements put on the undo stack. Each object contains complete
+paragraphs from some cell and sufficient information to restore the cursor
+state.
+
+The cell is given by a DocIterator pointing to this cell, the 'interesting'
+range of paragraphs by counting them from begin and end of cell,
+respectively.
+
+The cursor is also given as DocIterator and should point to some place in
+the stored paragraph range. In case of math, we simply store the whole
+cell, as there usually is just a simple paragraph in a cell.
+
+The idea is to store the contents of 'interesting' paragraphs in some
+structure ('Undo') _before_ it is changed in some edit operation.
+Obviously, the stored ranged should be as small as possible. However, it
+there is a lower limit: The StableDocIterator pointing stored in the undo
+class must be valid after the changes, too, as it will used as a pointer
+where to insert the stored bits when performining undo.
+
+*/
+struct UndoElement
+{
+ /// Which kind of operation are we recording for?
+ UndoKind kind;
+ /// the position of the cursor
+ StableDocIterator cursor;
+ /// the position of the cell described
+ StableDocIterator cell;
+ /// counted from begin of cell
+ pit_type from;
+ /// complement to end of this cell
+ pit_type end;
+ /// the contents of the saved Paragraphs (for texted)
+ ParagraphList * pars;
+ /// the contents of the saved MathData (for mathed)
+ MathData * array;
+ /// Only used in case of full backups
+ BufferParams bparams;
+ /// Only used in case of full backups
+ bool isFullBuffer;
+};
+
+
+struct Undo::Private
+{
+ Private(Buffer & buffer): buffer_(buffer) {}
+
+ // Returns false if no undo possible.
+ bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
+ ///
+ void doRecordUndo(UndoKind kind,
+ DocIterator const & cell,
+ pit_type first_pit,
+ pit_type last_pit,
+ DocIterator const & cur,
+ bool isFullBuffer,
+ bool isUndoOperation);
+
+ ///
+ void recordUndo(UndoKind kind,
+ DocIterator & cur,
+ pit_type first_pit,
+ pit_type last_pit);
+
+ ///
+ Buffer & buffer_;
+ /// Undo stack.
+ limited_stack<UndoElement> undostack;
+ /// Redo stack.
+ limited_stack<UndoElement> redostack;
+
+ /// The flag used by Undo::finishUndo().
+ bool undo_finished;
+};
+
+
+Undo::Undo(Buffer & buffer): d(new Undo::Private(buffer))
+{
+}
+
+
+Undo::~Undo()
+{
+ delete d;
+}
-namespace {
+bool Undo::hasUndoStack() const
+{
+ return !d->undostack.empty();
+}
+
+
+bool Undo::hasRedoStack() const
+{
+ return !d->redostack.empty();
+}
-/// The flag used by finishUndo().
-bool undo_finished;
-std::ostream & operator<<(std::ostream & os, Undo const & undo)
+
+namespace {
+
+ostream & operator<<(ostream & os, UndoElement const & undo)
{
return os << " from: " << undo.from << " end: " << undo.end
<< " cell:\n" << undo.cell
return i1 == tmpi2;
}
+} // namespace anon
+
-void doRecordUndo(Undo::undo_kind kind,
+void Undo::Private::doRecordUndo(UndoKind kind,
DocIterator const & cell,
pit_type first_pit, pit_type last_pit,
DocIterator const & cur,
- BufferParams const & bparams,
bool isFullBuffer,
- limited_stack<Undo> & stack)
+ bool isUndoOperation)
{
if (first_pit > last_pit)
- std::swap(first_pit, last_pit);
+ swap(first_pit, last_pit);
// create the position information of the Undo entry
- Undo undo;
+ UndoElement undo;
undo.array = 0;
undo.pars = 0;
undo.kind = kind;
undo.cell = cell;
undo.cursor = cur;
- undo.bparams = bparams ;
+ undo.bparams = buffer_.params();
undo.isFullBuffer = isFullBuffer;
//lyxerr << "recordUndo: cur: " << cur << endl;
//lyxerr << "recordUndo: pos: " << cur.pos() << endl;
undo.from = first_pit;
undo.end = cell.lastpit() - last_pit;
+ limited_stack<UndoElement> & stack = isUndoOperation ?
+ undostack : redostack;
+
// 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
+ && kind != ATOMIC_UNDO
&& !stack.empty()
&& samePar(stack.top().cell, undo.cell)
&& stack.top().kind == undo.kind
// fill in the real data to be saved
if (cell.inMathed()) {
// simply use the whole cell
- undo.array = new MathArray(cell.cell());
+ undo.array = new MathData(cell.cell());
} else {
// some more effort needed here as 'the whole cell' of the
- // main LyXText _is_ the whole document.
+ // main Text _is_ the whole document.
// record the relevant paragraphs
- LyXText const * text = cell.text();
+ Text const * text = cell.text();
BOOST_ASSERT(text);
ParagraphList const & plist = text->paragraphs();
ParagraphList::const_iterator first = plist.begin();
// push the undo entry to undo stack
stack.push(undo);
- //lyxerr << "undo record: " << stack.top() << std::endl;
+ //lyxerr << "undo record: " << stack.top() << endl;
// next time we'll try again to combine entries if possible
undo_finished = false;
}
-void recordUndo(Undo::undo_kind kind,
- LCursor & cur, pit_type first_pit, pit_type last_pit,
- limited_stack<Undo> & stack)
+void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
+ pit_type first_pit, pit_type last_pit)
{
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);
-}
+ false, true);
+ undo_finished = false;
+ redostack.clear();
+ //lyxerr << "undostack:\n";
+ //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
+ // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
+}
-// Returns false if no undo possible.
-bool textUndoOrRedo(BufferView & bv,
- limited_stack<Undo> & stack, limited_stack<Undo> & otherstack)
+bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
{
- finishUndo();
+ undo_finished = true;
+
+ limited_stack<UndoElement> & stack = isUndoOperation ?
+ undostack : redostack;
- if (stack.empty()) {
+ if (stack.empty())
// Nothing to do.
return false;
- }
+
+ limited_stack<UndoElement> & otherstack = isUndoOperation ?
+ redostack : undostack;
// Adjust undo stack and get hold of current undo data.
- Undo undo = stack.top();
+ UndoElement 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());
+ DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
- doRecordUndo(Undo::ATOMIC, cell_dit,
- undo.from, cell_dit.lastpit() - undo.end, bv.cursor(),
- undo.bparams, undo.isFullBuffer,
- otherstack);
+ doRecordUndo(ATOMIC_UNDO, cell_dit,
+ undo.from, cell_dit.lastpit() - undo.end, cur,
+ undo.isFullBuffer, !isUndoOperation);
// This does the actual undo/redo.
- //lyxerr << "undo, performing: " << undo << std::endl;
- DocIterator dit = undo.cell.asDocIterator(&buf->inset());
+ //LYXERR0("undo, performing: " << undo);
+ bool labelsUpdateNeeded = false;
+ DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
if (undo.isFullBuffer) {
BOOST_ASSERT(undo.pars);
// This is a full document
- otherstack.top().bparams = buf->params();
- buf->params() = undo.bparams;
- std::swap(buf->paragraphs(), *undo.pars);
+ otherstack.top().bparams = buffer_.params();
+ buffer_.params() = undo.bparams;
+ swap(buffer_.paragraphs(), *undo.pars);
delete undo.pars;
undo.pars = 0;
} 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: " << *undo.array <<endl;
+ //LYXERR0("undo.array: " << *undo.array);
BOOST_ASSERT(undo.array);
dit.cell().swap(*undo.array);
delete undo.array;
undo.array = 0;
} else {
// Some finer machinery is needed here.
- LyXText * text = dit.text();
+ Text * text = dit.text();
BOOST_ASSERT(text);
BOOST_ASSERT(undo.pars);
ParagraphList & plist = text->paragraphs();
plist.insert(first, undo.pars->begin(), undo.pars->end());
delete undo.pars;
undo.pars = 0;
- updateLabels(*buf);
+ labelsUpdateNeeded = true;
}
BOOST_ASSERT(undo.pars == 0);
BOOST_ASSERT(undo.array == 0);
- // Set cursor
- LCursor & cur = bv.cursor();
- cur.setCursor(undo.cursor.asDocIterator(&buf->inset()));
- cur.selection() = false;
- cur.resetAnchor();
- cur.fixIfBroken();
- finishUndo();
-
+ cur = undo.cursor.asDocIterator(&buffer_.inset());
+
+ if (labelsUpdateNeeded)
+ updateLabels(buffer_);
+ undo_finished = true;
return true;
}
-} // namespace anon
-
-void finishUndo()
+void Undo::finishUndo()
{
// Make sure the next operation will be stored.
- undo_finished = true;
-}
-
-
-bool textUndo(BufferView & bv)
-{
- return textUndoOrRedo(bv, bv.buffer()->undostack(),
- bv.buffer()->redostack());
+ d->undo_finished = true;
}
-bool textRedo(BufferView & bv)
+bool Undo::textUndo(DocIterator & cur)
{
- return textUndoOrRedo(bv, bv.buffer()->redostack(),
- bv.buffer()->undostack());
+ return d->textUndoOrRedo(cur, true);
}
-void recordUndo(Undo::undo_kind kind,
- LCursor & cur, pit_type first, pit_type last)
+bool Undo::textRedo(DocIterator & cur)
{
- 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;
+ return d->textUndoOrRedo(cur, false);
}
-void recordUndo(LCursor & cur, Undo::undo_kind kind)
+void Undo::recordUndo(DocIterator & cur, UndoKind kind)
{
- recordUndo(kind, cur, cur.pit(), cur.pit());
+ d->recordUndo(kind, cur, cur.pit(), cur.pit());
}
-void recordUndoInset(LCursor & cur, Undo::undo_kind kind)
+void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
{
- LCursor c = cur;
- c.pop();
- Buffer * buf = cur.bv().buffer();
- doRecordUndo(kind, c, c.pit(), c.pit(), cur,
- buf->params(), false, buf->undostack());
+ DocIterator c = cur;
+ c.pop_back();
+ d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
}
-void recordUndoSelection(LCursor & cur, Undo::undo_kind kind)
+void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
{
- recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit());
+ d->recordUndo(kind, cur, cur.pit(), from);
}
-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,
+void Undo::recordUndo(DocIterator & cur, UndoKind kind,
pit_type from, pit_type to)
{
- recordUndo(kind, cur, from, to);
+ d->recordUndo(kind, cur, from, to);
}
-void recordUndoFullDocument(BufferView * bv)
+void Undo::recordUndoFullDocument(DocIterator & cur)
{
- Buffer * buf = bv->buffer();
- doRecordUndo(
- Undo::ATOMIC,
- doc_iterator_begin(buf->inset()),
- 0, buf->paragraphs().size() - 1,
- bv->cursor(),
- buf->params(),
+ d->doRecordUndo(
+ ATOMIC_UNDO,
+ doc_iterator_begin(d->buffer_.inset()),
+ 0, d->buffer_.paragraphs().size() - 1,
+ cur,
true,
- buf->undostack()
+ true
);
- undo_finished = false;
}