3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Asger Alstrup
7 * \author Lars Gullik Bjønnes
10 * \author Jürgen Vigna
11 * \author Abdelrazak Younes
13 * Full author contact details are available in file CREDITS.
21 #include "BufferParams.h"
22 #include "buffer_funcs.h"
23 #include "DocIterator.h"
24 #include "Paragraph.h"
25 #include "ParagraphList.h"
28 #include "mathed/MathSupport.h"
29 #include "mathed/MathData.h"
31 #include "insets/Inset.h"
33 #include "support/assert.h"
34 #include "support/debug.h"
41 using namespace lyx::support;
47 These are the elements put on the undo stack. Each object contains complete
48 paragraphs from some cell and sufficient information to restore the cursor
51 The cell is given by a DocIterator pointing to this cell, the 'interesting'
52 range of paragraphs by counting them from begin and end of cell,
55 The cursor is also given as DocIterator and should point to some place in
56 the stored paragraph range. In case of math, we simply store the whole
57 cell, as there usually is just a simple paragraph in a cell.
59 The idea is to store the contents of 'interesting' paragraphs in some
60 structure ('Undo') _before_ it is changed in some edit operation.
61 Obviously, the stored ranged should be as small as possible. However, it
62 there is a lower limit: The StableDocIterator pointing stored in the undo
63 class must be valid after the changes, too, as it will used as a pointer
64 where to insert the stored bits when performining undo.
70 /// Which kind of operation are we recording for?
72 /// the position of the cursor
73 StableDocIterator cursor;
74 /// the position of the cell described
75 StableDocIterator cell;
76 /// counted from begin of cell
78 /// complement to end of this cell
80 /// the contents of the saved Paragraphs (for texted)
82 /// the contents of the saved MathData (for mathed)
84 /// Only used in case of full backups
86 /// Only used in case of full backups
91 class UndoElementStack
94 /// limit is the maximum size of the stack
95 UndoElementStack(size_t limit = 100) { limit_ = limit; }
96 /// limit is the maximum size of the stack
97 ~UndoElementStack() { clear(); }
99 /// Return the top element.
100 UndoElement & top() { return c_.front(); }
102 /// Pop and throw away the top element.
103 void pop() { c_.pop_front(); }
105 /// Return true if the stack is empty.
106 bool empty() const { return c_.empty(); }
108 /// Clear all elements, deleting them.
110 for (size_t i = 0; i != c_.size(); ++i) {
117 /// Push an item on to the stack, deleting the
118 /// bottom item on overflow.
119 void push(UndoElement const & v) {
121 if (c_.size() > limit_)
126 /// Internal contents.
127 std::deque<UndoElement> c_;
128 /// The maximum number elements stored.
135 Private(Buffer & buffer) : buffer_(buffer), undo_finished_(true) {}
137 // Returns false if no undo possible.
138 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
140 void doRecordUndo(UndoKind kind,
141 DocIterator const & cell,
144 DocIterator const & cur,
146 bool isUndoOperation);
149 void recordUndo(UndoKind kind,
157 UndoElementStack undostack_;
159 UndoElementStack redostack_;
161 /// The flag used by Undo::finishUndo().
166 /////////////////////////////////////////////////////////////////////
170 /////////////////////////////////////////////////////////////////////
173 Undo::Undo(Buffer & buffer)
174 : d(new Undo::Private(buffer))
184 bool Undo::hasUndoStack() const
186 return !d->undostack_.empty();
190 bool Undo::hasRedoStack() const
192 return !d->redostack_.empty();
197 static ostream & operator<<(ostream & os, UndoElement const & undo)
199 return os << " from: " << undo.from << " end: " << undo.end
200 << " cell:\n" << undo.cell
201 << " cursor:\n" << undo.cursor;
206 static bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
208 StableDocIterator tmpi2 = i2;
209 tmpi2.pos() = i1.pos();
214 /////////////////////////////////////////////////////////////////////
218 ///////////////////////////////////////////////////////////////////////
220 void Undo::Private::doRecordUndo(UndoKind kind,
221 DocIterator const & cell,
222 pit_type first_pit, pit_type last_pit,
223 DocIterator const & cur,
225 bool isUndoOperation)
227 if (first_pit > last_pit)
228 swap(first_pit, last_pit);
229 // create the position information of the Undo entry
236 undo.bparams = buffer_.params();
237 undo.isFullBuffer = isFullBuffer;
238 //lyxerr << "recordUndo: cur: " << cur << endl;
239 //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
240 //lyxerr << "recordUndo: cell: " << cell << endl;
241 undo.from = first_pit;
242 undo.end = cell.lastpit() - last_pit;
244 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
246 // Undo::ATOMIC are always recorded (no overlapping there).
247 // As nobody wants all removed character appear one by one when undoing,
248 // we want combine 'similar' non-ATOMIC undo recordings to one.
250 && kind != ATOMIC_UNDO
252 && samePar(stack.top().cell, undo.cell)
253 && stack.top().kind == undo.kind
254 && stack.top().from == undo.from
255 && stack.top().end == undo.end)
258 // fill in the real data to be saved
259 if (cell.inMathed()) {
260 // simply use the whole cell
261 undo.array = new MathData(cell.cell());
263 // some more effort needed here as 'the whole cell' of the
264 // main Text _is_ the whole document.
265 // record the relevant paragraphs
266 Text const * text = cell.text();
268 ParagraphList const & plist = text->paragraphs();
269 ParagraphList::const_iterator first = plist.begin();
270 advance(first, first_pit);
271 ParagraphList::const_iterator last = plist.begin();
272 advance(last, last_pit + 1);
273 undo.pars = new ParagraphList(first, last);
276 // push the undo entry to undo stack
278 //lyxerr << "undo record: " << stack.top() << endl;
280 // next time we'll try again to combine entries if possible
281 undo_finished_ = false;
285 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
286 pit_type first_pit, pit_type last_pit)
288 LASSERT(first_pit <= cur.lastpit(), /**/);
289 LASSERT(last_pit <= cur.lastpit(), /**/);
291 doRecordUndo(kind, cur, first_pit, last_pit, cur,
294 undo_finished_ = false;
296 //lyxerr << "undostack:\n";
297 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
298 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
302 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
304 undo_finished_ = true;
306 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
312 UndoElementStack & otherstack = isUndoOperation ? redostack_ : undostack_;
314 // Adjust undo stack and get hold of current undo data.
315 UndoElement undo = stack.top();
318 // We will store in otherstack the part of the document under 'undo'
319 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
321 doRecordUndo(ATOMIC_UNDO, cell_dit,
322 undo.from, cell_dit.lastpit() - undo.end, cur,
323 undo.isFullBuffer, !isUndoOperation);
325 // This does the actual undo/redo.
326 //LYXERR0("undo, performing: " << undo);
327 bool labelsUpdateNeeded = false;
328 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
329 if (undo.isFullBuffer) {
330 LASSERT(undo.pars, /**/);
331 // This is a full document
332 otherstack.top().bparams = buffer_.params();
333 buffer_.params() = undo.bparams;
334 swap(buffer_.paragraphs(), *undo.pars);
337 } else if (dit.inMathed()) {
338 // We stored the full cell here as there is not much to be
339 // gained by storing just 'a few' paragraphs (most if not
340 // all math inset cells have just one paragraph!)
341 //LYXERR0("undo.array: " << *undo.array);
342 LASSERT(undo.array, /**/);
343 dit.cell().swap(*undo.array);
347 // Some finer machinery is needed here.
348 Text * text = dit.text();
350 LASSERT(undo.pars, /**/);
351 ParagraphList & plist = text->paragraphs();
353 // remove new stuff between first and last
354 ParagraphList::iterator first = plist.begin();
355 advance(first, undo.from);
356 ParagraphList::iterator last = plist.begin();
357 advance(last, plist.size() - undo.end);
358 plist.erase(first, last);
360 // re-insert old stuff instead
361 first = plist.begin();
362 advance(first, undo.from);
364 // this ugly stuff is needed until we get rid of the
365 // inset_owner backpointer
366 ParagraphList::iterator pit = undo.pars->begin();
367 ParagraphList::iterator const end = undo.pars->end();
368 for (; pit != end; ++pit)
369 pit->setInsetOwner(dit.realInset());
370 plist.insert(first, undo.pars->begin(), undo.pars->end());
373 labelsUpdateNeeded = true;
375 LASSERT(undo.pars == 0, /**/);
376 LASSERT(undo.array == 0, /**/);
378 cur = undo.cursor.asDocIterator(&buffer_.inset());
380 if (labelsUpdateNeeded)
381 updateLabels(buffer_);
382 undo_finished_ = true;
387 void Undo::finishUndo()
389 // Make sure the next operation will be stored.
390 d->undo_finished_ = true;
394 bool Undo::textUndo(DocIterator & cur)
396 return d->textUndoOrRedo(cur, true);
400 bool Undo::textRedo(DocIterator & cur)
402 return d->textUndoOrRedo(cur, false);
406 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
408 d->recordUndo(kind, cur, cur.pit(), cur.pit());
412 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
416 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
420 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
422 d->recordUndo(kind, cur, cur.pit(), from);
426 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
427 pit_type from, pit_type to)
429 d->recordUndo(kind, cur, from, to);
433 void Undo::recordUndoFullDocument(DocIterator & cur)
437 doc_iterator_begin(d->buffer_.inset()),
438 0, d->buffer_.paragraphs().size() - 1,