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), bparams(bp), isFullBuffer(ifb)
78 /// Which kind of operation are we recording for?
80 /// the position of the cursor
81 StableDocIterator cursor;
82 /// the position of the cell described
83 StableDocIterator cell;
84 /// counted from begin of cell
86 /// complement to end of this cell
88 /// the contents of the saved Paragraphs (for texted)
90 /// the contents of the saved MathData (for mathed)
92 /// Only used in case of full backups
94 /// Only used in case of full backups
97 /// Protect construction
102 class UndoElementStack
105 /// limit is the maximum size of the stack
106 UndoElementStack(size_t limit = 100) { limit_ = limit; }
107 /// limit is the maximum size of the stack
108 ~UndoElementStack() { clear(); }
110 /// Return the top element.
111 UndoElement & top() { return c_.front(); }
113 /// Pop and throw away the top element.
114 void pop() { c_.pop_front(); }
116 /// Return true if the stack is empty.
117 bool empty() const { return c_.empty(); }
119 /// Clear all elements, deleting them.
121 for (size_t i = 0; i != c_.size(); ++i) {
128 /// Push an item on to the stack, deleting the
129 /// bottom item on overflow.
130 void push(UndoElement const & v) {
132 if (c_.size() > limit_)
137 /// Internal contents.
138 std::deque<UndoElement> c_;
139 /// The maximum number elements stored.
146 Private(Buffer & buffer) : buffer_(buffer), undo_finished_(true) {}
148 // Returns false if no undo possible.
149 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
151 void doRecordUndo(UndoKind kind,
152 DocIterator const & cell,
155 DocIterator const & cur,
157 bool isUndoOperation);
160 void recordUndo(UndoKind kind,
168 UndoElementStack undostack_;
170 UndoElementStack redostack_;
172 /// The flag used by Undo::finishUndo().
177 /////////////////////////////////////////////////////////////////////
181 /////////////////////////////////////////////////////////////////////
184 Undo::Undo(Buffer & buffer)
185 : d(new Undo::Private(buffer))
195 bool Undo::hasUndoStack() const
197 return !d->undostack_.empty();
201 bool Undo::hasRedoStack() const
203 return !d->redostack_.empty();
207 static bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
209 StableDocIterator tmpi2 = i2;
210 tmpi2.pos() = i1.pos();
215 /////////////////////////////////////////////////////////////////////
219 ///////////////////////////////////////////////////////////////////////
221 void Undo::Private::doRecordUndo(UndoKind kind,
222 DocIterator const & cell,
223 pit_type first_pit, pit_type last_pit,
224 DocIterator const & cur,
226 bool isUndoOperation)
228 if (first_pit > last_pit)
229 swap(first_pit, last_pit);
230 // create the position information of the Undo entry
231 UndoElement undo(kind, cur, cell, first_pit, cell.lastpit() - last_pit, 0, 0,
232 buffer_.params(), isFullBuffer);
233 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
235 // Undo::ATOMIC are always recorded (no overlapping there).
236 // As nobody wants all removed character appear one by one when undoing,
237 // we want combine 'similar' non-ATOMIC undo recordings to one.
239 && kind != ATOMIC_UNDO
241 && samePar(stack.top().cell, undo.cell)
242 && stack.top().kind == undo.kind
243 && stack.top().from == undo.from
244 && stack.top().end == undo.end)
247 // fill in the real data to be saved
248 if (cell.inMathed()) {
249 // simply use the whole cell
250 undo.array = new MathData(cell.cell());
252 // some more effort needed here as 'the whole cell' of the
253 // main Text _is_ the whole document.
254 // record the relevant paragraphs
255 Text const * text = cell.text();
257 ParagraphList const & plist = text->paragraphs();
258 ParagraphList::const_iterator first = plist.begin();
259 advance(first, first_pit);
260 ParagraphList::const_iterator last = plist.begin();
261 advance(last, last_pit + 1);
262 undo.pars = new ParagraphList(first, last);
265 // push the undo entry to undo stack
267 //lyxerr << "undo record: " << stack.top() << endl;
269 // next time we'll try again to combine entries if possible
270 undo_finished_ = false;
274 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
275 pit_type first_pit, pit_type last_pit)
277 LASSERT(first_pit <= cur.lastpit(), /**/);
278 LASSERT(last_pit <= cur.lastpit(), /**/);
280 doRecordUndo(kind, cur, first_pit, last_pit, cur,
283 undo_finished_ = false;
285 //lyxerr << "undostack:\n";
286 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
287 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
291 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
293 undo_finished_ = true;
295 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
301 UndoElementStack & otherstack = isUndoOperation ? redostack_ : undostack_;
303 // Adjust undo stack and get hold of current undo data.
304 UndoElement undo = stack.top();
307 // We will store in otherstack the part of the document under 'undo'
308 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
310 doRecordUndo(ATOMIC_UNDO, cell_dit,
311 undo.from, cell_dit.lastpit() - undo.end, cur,
312 undo.isFullBuffer, !isUndoOperation);
314 // This does the actual undo/redo.
315 //LYXERR0("undo, performing: " << undo);
316 bool labelsUpdateNeeded = false;
317 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
318 if (undo.isFullBuffer) {
319 LASSERT(undo.pars, /**/);
320 // This is a full document
321 otherstack.top().bparams = buffer_.params();
322 buffer_.params() = undo.bparams;
323 swap(buffer_.paragraphs(), *undo.pars);
326 } else if (dit.inMathed()) {
327 // We stored the full cell here as there is not much to be
328 // gained by storing just 'a few' paragraphs (most if not
329 // all math inset cells have just one paragraph!)
330 //LYXERR0("undo.array: " << *undo.array);
331 LASSERT(undo.array, /**/);
332 dit.cell().swap(*undo.array);
336 // Some finer machinery is needed here.
337 Text * text = dit.text();
339 LASSERT(undo.pars, /**/);
340 ParagraphList & plist = text->paragraphs();
342 // remove new stuff between first and last
343 ParagraphList::iterator first = plist.begin();
344 advance(first, undo.from);
345 ParagraphList::iterator last = plist.begin();
346 advance(last, plist.size() - undo.end);
347 plist.erase(first, last);
349 // re-insert old stuff instead
350 first = plist.begin();
351 advance(first, undo.from);
353 // this ugly stuff is needed until we get rid of the
354 // inset_owner backpointer
355 ParagraphList::iterator pit = undo.pars->begin();
356 ParagraphList::iterator const end = undo.pars->end();
357 for (; pit != end; ++pit)
358 pit->setInsetOwner(dit.realInset());
359 plist.insert(first, undo.pars->begin(), undo.pars->end());
362 labelsUpdateNeeded = true;
364 LASSERT(undo.pars == 0, /**/);
365 LASSERT(undo.array == 0, /**/);
367 cur = undo.cursor.asDocIterator(&buffer_.inset());
369 if (labelsUpdateNeeded)
370 updateLabels(buffer_);
371 undo_finished_ = true;
376 void Undo::finishUndo()
378 // Make sure the next operation will be stored.
379 d->undo_finished_ = true;
383 bool Undo::textUndo(DocIterator & cur)
385 return d->textUndoOrRedo(cur, true);
389 bool Undo::textRedo(DocIterator & cur)
391 return d->textUndoOrRedo(cur, false);
395 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
397 d->recordUndo(kind, cur, cur.pit(), cur.pit());
401 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
405 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
409 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
411 d->recordUndo(kind, cur, cur.pit(), from);
415 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
416 pit_type from, pit_type to)
418 d->recordUndo(kind, cur, from, to);
422 void Undo::recordUndoFullDocument(DocIterator & cur)
426 doc_iterator_begin(d->buffer_.inset()),
427 0, d->buffer_.paragraphs().size() - 1,