+/**
+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
+{
+ ///
+ UndoElement(UndoKind kin, StableDocIterator const & cur,
+ StableDocIterator const & cel,
+ pit_type fro, pit_type en, ParagraphList * pl,
+ MathData * ar, BufferParams const & bp,
+ bool ifb, bool lc, size_t gid) :
+ kind(kin), cursor(cur), cell(cel), from(fro), end(en),
+ pars(pl), array(ar), bparams(0), isFullBuffer(ifb), lyx_clean(lc), group_id(gid)
+ {
+ if (isFullBuffer)
+ bparams = new BufferParams(bp);
+ }
+ ///
+ UndoElement(UndoElement const & ue)
+ {
+ kind = ue.kind;
+ cursor = ue.cursor;
+ cell = ue.cell;
+ from = ue.from;
+ end = ue.end;
+ pars = ue.pars;
+ array = ue.array;
+ bparams = ue.isFullBuffer
+ ? new BufferParams(*ue.bparams) : ue.bparams;
+ isFullBuffer = ue.isFullBuffer;
+ lyx_clean = ue.lyx_clean;
+ group_id = ue.group_id;
+ }
+ ///
+ ~UndoElement()
+ {
+ if (isFullBuffer)
+ delete bparams;
+ }
+ /// 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 const * bparams;
+ /// Only used in case of full backups
+ bool isFullBuffer;
+ /// Was the buffer clean at this point?
+ bool lyx_clean;
+ /// the element's group id
+ size_t group_id;
+private:
+ /// Protect construction
+ UndoElement();
+};
+
+
+class UndoElementStack
+{
+public:
+ /// limit is the maximum size of the stack
+ UndoElementStack(size_t limit = 100) { limit_ = limit; }
+ /// limit is the maximum size of the stack
+ ~UndoElementStack() { clear(); }
+
+ /// Return the top element.
+ UndoElement & top() { return c_.front(); }
+
+ /// Pop and throw away the top element.
+ void pop() { c_.pop_front(); }
+
+ /// Return true if the stack is empty.
+ bool empty() const { return c_.empty(); }
+
+ /// Clear all elements, deleting them.
+ void clear() {
+ for (size_t i = 0; i != c_.size(); ++i) {
+ delete c_[i].array;
+ delete c_[i].pars;
+ }
+ c_.clear();
+ }
+
+ /// Push an item on to the stack, deleting the bottom group on
+ /// overflow.
+ void push(UndoElement const & v) {
+ c_.push_front(v);
+ if (c_.size() > limit_) {
+ // remove a whole group at once.
+ const size_t gid = c_.back().group_id;
+ while (!c_.empty() && c_.back().group_id == gid)
+ c_.pop_back();
+ }
+ }
+
+ /// Mark all the elements of the stack as dirty
+ void markDirty() {
+ for (size_t i = 0; i != c_.size(); ++i)
+ c_[i].lyx_clean = false;
+ }
+
+private:
+ /// Internal contents.
+ std::deque<UndoElement> c_;
+ /// The maximum number elements stored.
+ size_t limit_;
+};
+
+
+struct Undo::Private
+{
+ Private(Buffer & buffer) : buffer_(buffer), undo_finished_(true),
+ group_id(0), group_level(0) {}
+
+ // Do one undo/redo step
+ void doTextUndoOrRedo(DocIterator & cur, UndoElementStack & stack, UndoElementStack & otherStack);
+ // Apply one undo/redo group. 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,
+ UndoElementStack & stack);
+ ///
+ void recordUndo(UndoKind kind,
+ DocIterator const & cur,
+ pit_type first_pit,
+ pit_type last_pit);
+
+ ///
+ Buffer & buffer_;
+ /// Undo stack.
+ UndoElementStack undostack_;
+ /// Redo stack.
+ UndoElementStack redostack_;
+
+ /// The flag used by Undo::finishUndo().
+ bool undo_finished_;
+
+ /// Current group Id.
+ size_t group_id;
+ /// Current group nesting nevel.
+ size_t group_level;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Undo
+//
+/////////////////////////////////////////////////////////////////////
+
+
+Undo::Undo(Buffer & buffer)
+ : d(new Undo::Private(buffer))
+{}
+
+
+Undo::~Undo()
+{
+ delete d;
+}
+