+/**
+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);
+ }