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/lassert.h"
34 #include "support/debug.h"
40 using namespace lyx::support;
46 These are the elements put on the undo stack. Each object contains complete
47 paragraphs from some cell and sufficient information to restore the cursor
50 The cell is given by a DocIterator pointing to this cell, the 'interesting'
51 range of paragraphs by counting them from begin and end of cell,
54 The cursor is also given as DocIterator and should point to some place in
55 the stored paragraph range. In case of math, we simply store the whole
56 cell, as there usually is just a simple paragraph in a cell.
58 The idea is to store the contents of 'interesting' paragraphs in some
59 structure ('Undo') _before_ it is changed in some edit operation.
60 Obviously, the stored ranged should be as small as possible. However, it
61 there is a lower limit: The StableDocIterator pointing stored in the undo
62 class must be valid after the changes, too, as it will used as a pointer
63 where to insert the stored bits when performining undo.
70 UndoElement(UndoKind kin, StableDocIterator const & cur,
71 StableDocIterator const & cel,
72 pit_type fro, pit_type en, ParagraphList * pl,
73 MathData * ar, BufferParams const & bp,
75 kind(kin), cursor(cur), cell(cel), from(fro), end(en),
76 pars(pl), array(ar), isFullBuffer(ifb)
79 bparams = new BufferParams(bp);
82 ~UndoElement() { delete bparams; }
83 /// Which kind of operation are we recording for?
85 /// the position of the cursor
86 StableDocIterator cursor;
87 /// the position of the cell described
88 StableDocIterator cell;
89 /// counted from begin of cell
91 /// complement to end of this cell
93 /// the contents of the saved Paragraphs (for texted)
95 /// the contents of the saved MathData (for mathed)
97 /// Only used in case of full backups
98 BufferParams const * bparams;
99 /// Only used in case of full backups
102 /// Protect construction
107 class UndoElementStack
110 /// limit is the maximum size of the stack
111 UndoElementStack(size_t limit = 100) { limit_ = limit; }
112 /// limit is the maximum size of the stack
113 ~UndoElementStack() { clear(); }
115 /// Return the top element.
116 UndoElement & top() { return c_.front(); }
118 /// Pop and throw away the top element.
119 void pop() { c_.pop_front(); }
121 /// Return true if the stack is empty.
122 bool empty() const { return c_.empty(); }
124 /// Clear all elements, deleting them.
126 for (size_t i = 0; i != c_.size(); ++i) {
133 /// Push an item on to the stack, deleting the
134 /// bottom item on overflow.
135 void push(UndoElement const & v) {
137 if (c_.size() > limit_)
142 /// Internal contents.
143 std::deque<UndoElement> c_;
144 /// The maximum number elements stored.
151 Private(Buffer & buffer) : buffer_(buffer), undo_finished_(true) {}
153 // Returns false if no undo possible.
154 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
156 void doRecordUndo(UndoKind kind,
157 DocIterator const & cell,
160 DocIterator const & cur,
162 bool isUndoOperation);
165 void recordUndo(UndoKind kind,
173 UndoElementStack undostack_;
175 UndoElementStack redostack_;
177 /// The flag used by Undo::finishUndo().
182 /////////////////////////////////////////////////////////////////////
186 /////////////////////////////////////////////////////////////////////
189 Undo::Undo(Buffer & buffer)
190 : d(new Undo::Private(buffer))
200 bool Undo::hasUndoStack() const
202 return !d->undostack_.empty();
206 bool Undo::hasRedoStack() const
208 return !d->redostack_.empty();
212 static bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
214 StableDocIterator tmpi2 = i2;
215 tmpi2.pos() = i1.pos();
220 /////////////////////////////////////////////////////////////////////
224 ///////////////////////////////////////////////////////////////////////
226 void Undo::Private::doRecordUndo(UndoKind kind,
227 DocIterator const & cell,
228 pit_type first_pit, pit_type last_pit,
229 DocIterator const & cur,
231 bool isUndoOperation)
233 if (first_pit > last_pit)
234 swap(first_pit, last_pit);
236 // Undo::ATOMIC are always recorded (no overlapping there).
237 // As nobody wants all removed character appear one by one when undoing,
238 // we want combine 'similar' non-ATOMIC undo recordings to one.
239 pit_type from = first_pit;
240 pit_type end = cell.lastpit() - last_pit;
241 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
243 && kind != ATOMIC_UNDO
245 && samePar(stack.top().cell, cell)
246 && stack.top().kind == kind
247 && stack.top().from == from
248 && stack.top().end == end)
251 // create the position information of the Undo entry
252 UndoElement undo(kind, cur, cell, from, end, 0, 0,
253 buffer_.params(), isFullBuffer);
255 // fill in the real data to be saved
256 if (cell.inMathed()) {
257 // simply use the whole cell
258 undo.array = new MathData(cell.cell());
260 // some more effort needed here as 'the whole cell' of the
261 // main Text _is_ the whole document.
262 // record the relevant paragraphs
263 Text const * text = cell.text();
265 ParagraphList const & plist = text->paragraphs();
266 ParagraphList::const_iterator first = plist.begin();
267 advance(first, first_pit);
268 ParagraphList::const_iterator last = plist.begin();
269 advance(last, last_pit + 1);
270 undo.pars = new ParagraphList(first, last);
273 // push the undo entry to undo stack
275 //lyxerr << "undo record: " << stack.top() << endl;
277 // next time we'll try again to combine entries if possible
278 undo_finished_ = false;
282 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
283 pit_type first_pit, pit_type last_pit)
285 LASSERT(first_pit <= cur.lastpit(), /**/);
286 LASSERT(last_pit <= cur.lastpit(), /**/);
288 doRecordUndo(kind, cur, first_pit, last_pit, cur,
291 undo_finished_ = false;
293 //lyxerr << "undostack:\n";
294 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
295 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
299 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
301 undo_finished_ = true;
303 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
309 UndoElementStack & otherstack = isUndoOperation ? redostack_ : undostack_;
311 // Adjust undo stack and get hold of current undo data.
312 UndoElement & undo = stack.top();
313 // We'll pop the stack only when we're done with this element. So do NOT
314 // try to return early.
316 // We will store in otherstack the part of the document under 'undo'
317 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
319 doRecordUndo(ATOMIC_UNDO, cell_dit,
320 undo.from, cell_dit.lastpit() - undo.end, cur,
321 undo.isFullBuffer, !isUndoOperation);
323 // This does the actual undo/redo.
324 //LYXERR0("undo, performing: " << undo);
325 bool labelsUpdateNeeded = false;
326 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
327 if (undo.isFullBuffer) {
328 LASSERT(undo.pars, /**/);
329 // This is a full document
330 delete otherstack.top().bparams;
331 otherstack.top().bparams = new BufferParams(buffer_.params());
332 buffer_.params() = *undo.bparams;
333 swap(buffer_.paragraphs(), *undo.pars);
336 } else if (dit.inMathed()) {
337 // We stored the full cell here as there is not much to be
338 // gained by storing just 'a few' paragraphs (most if not
339 // all math inset cells have just one paragraph!)
340 //LYXERR0("undo.array: " << *undo.array);
341 LASSERT(undo.array, /**/);
342 dit.cell().swap(*undo.array);
346 // Some finer machinery is needed here.
347 Text * text = dit.text();
349 LASSERT(undo.pars, /**/);
350 ParagraphList & plist = text->paragraphs();
352 // remove new stuff between first and last
353 ParagraphList::iterator first = plist.begin();
354 advance(first, undo.from);
355 ParagraphList::iterator last = plist.begin();
356 advance(last, plist.size() - undo.end);
357 plist.erase(first, last);
359 // re-insert old stuff instead
360 first = plist.begin();
361 advance(first, undo.from);
363 // this ugly stuff is needed until we get rid of the
364 // inset_owner backpointer
365 ParagraphList::iterator pit = undo.pars->begin();
366 ParagraphList::iterator const end = undo.pars->end();
367 for (; pit != end; ++pit)
368 pit->setInsetOwner(dit.realInset());
369 plist.insert(first, undo.pars->begin(), undo.pars->end());
372 labelsUpdateNeeded = true;
374 LASSERT(undo.pars == 0, /**/);
375 LASSERT(undo.array == 0, /**/);
377 cur = undo.cursor.asDocIterator(&buffer_.inset());
378 // Now that we're done with undo, we pop it off the stack.
381 if (labelsUpdateNeeded)
382 updateLabels(buffer_);
383 undo_finished_ = true;
388 void Undo::finishUndo()
390 // Make sure the next operation will be stored.
391 d->undo_finished_ = true;
395 bool Undo::textUndo(DocIterator & cur)
397 return d->textUndoOrRedo(cur, true);
401 bool Undo::textRedo(DocIterator & cur)
403 return d->textUndoOrRedo(cur, false);
407 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
409 d->recordUndo(kind, cur, cur.pit(), cur.pit());
413 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
417 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
421 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
423 d->recordUndo(kind, cur, cur.pit(), from);
427 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
428 pit_type from, pit_type to)
430 d->recordUndo(kind, cur, from, to);
434 void Undo::recordUndoFullDocument(DocIterator & cur)
438 doc_iterator_begin(d->buffer_.inset()),
439 0, d->buffer_.paragraphs().size() - 1,