+/**
+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 range should be as small as possible. However,
+there is a lower limit: The StableDocIterator 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, CursorData const & cb,
+ StableDocIterator const & cel,
+ pit_type fro, pit_type en, ParagraphList * pl, MathData * ar,
+ bool lc, size_t gid) :
+ kind(kin), cur_before(cb), cell(cel), from(fro), end(en),
+ pars(pl), array(ar), bparams(0),
+ lyx_clean(lc), group_id(gid), time(current_time())
+ {
+ }
+ ///
+ UndoElement(CursorData const & cb, BufferParams const & bp,
+ bool lc, size_t gid) :
+ kind(ATOMIC_UNDO), cur_before(cb), cell(), from(0), end(0),
+ pars(0), array(0), bparams(new BufferParams(bp)),
+ lyx_clean(lc), group_id(gid), time(current_time())
+ {
+ }
+ ///
+ UndoElement(UndoElement const & ue) : time(current_time())
+ {
+ kind = ue.kind;
+ cur_before = ue.cur_before;
+ cur_after = ue.cur_after;
+ cell = ue.cell;
+ from = ue.from;
+ end = ue.end;
+ pars = ue.pars;
+ array = ue.array;
+ bparams = ue.bparams
+ ? new BufferParams(*ue.bparams) : 0;
+ lyx_clean = ue.lyx_clean;
+ group_id = ue.group_id;
+ }
+ ///
+ ~UndoElement()
+ {
+ if (bparams)
+ delete bparams;
+ }
+ /// Which kind of operation are we recording for?
+ UndoKind kind;
+ /// the position of the cursor before recordUndo
+ CursorData cur_before;
+ /// the position of the cursor at the end of the undo group
+ CursorData cur_after;
+ /// 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 params undo
+ BufferParams const * bparams;
+ /// Was the buffer clean at this point?
+ bool lyx_clean;
+ /// the element's group id
+ size_t group_id;
+ /// timestamp
+ time_t time;
+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) {
+ // Remove some entries if the limit has been reached.
+ // However, if the only group on the stack is the one
+ // we are currently populating, do nothing.
+ if (c_.size() >= limit_
+ && c_.front().group_id != v.group_id) {
+ // 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();
+ }
+ c_.push_front(v);
+ }
+
+ /// 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 doUndoRedoAction(CursorData & cur, UndoElementStack & stack,
+ UndoElementStack & otherStack);
+ // Apply one undo/redo group. Returns false if no undo possible.
+ bool undoRedoAction(CursorData & cur, bool isUndoOperation);
+
+ ///
+ void doRecordUndo(UndoKind kind,
+ DocIterator const & cell,
+ pit_type first_pit,
+ pit_type last_pit,
+ CursorData const & cur,
+ UndoElementStack & stack);
+ ///
+ void recordUndo(UndoKind kind,
+ DocIterator const & cell,
+ pit_type first_pit,
+ pit_type last_pit,
+ CursorData const & cur);
+ ///
+ void doRecordUndoBufferParams(CursorData const & cur, UndoElementStack & stack);
+ ///
+ void recordUndoBufferParams(CursorData const & cur);
+
+ ///
+ 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_;
+ /// the position of cursor before the group was created
+ CursorData group_cur_before_;
+};
+
+
+/////////////////////////////////////////////////////////////////////
+//
+// Undo
+//
+/////////////////////////////////////////////////////////////////////
+
+
+Undo::Undo(Buffer & buffer)
+ : d(new Undo::Private(buffer))
+{}
+
+
+Undo::~Undo()
+{
+ delete d;
+}