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);
231 // Undo::ATOMIC are always recorded (no overlapping there).
232 // As nobody wants all removed character appear one by one when undoing,
233 // we want combine 'similar' non-ATOMIC undo recordings to one.
234 pit_type from = first_pit;
235 pit_type end = cell.lastpit() - last_pit;
236 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
238 && kind != ATOMIC_UNDO
240 && samePar(stack.top().cell, cell)
241 && stack.top().kind == kind
242 && stack.top().from == from
243 && stack.top().end == end)
246 // create the position information of the Undo entry
247 UndoElement undo(kind, cur, cell, from, end, 0, 0,
248 buffer_.params(), isFullBuffer);
250 // fill in the real data to be saved
251 if (cell.inMathed()) {
252 // simply use the whole cell
253 undo.array = new MathData(cell.cell());
255 // some more effort needed here as 'the whole cell' of the
256 // main Text _is_ the whole document.
257 // record the relevant paragraphs
258 Text const * text = cell.text();
260 ParagraphList const & plist = text->paragraphs();
261 ParagraphList::const_iterator first = plist.begin();
262 advance(first, first_pit);
263 ParagraphList::const_iterator last = plist.begin();
264 advance(last, last_pit + 1);
265 undo.pars = new ParagraphList(first, last);
268 // push the undo entry to undo stack
270 //lyxerr << "undo record: " << stack.top() << endl;
272 // next time we'll try again to combine entries if possible
273 undo_finished_ = false;
277 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
278 pit_type first_pit, pit_type last_pit)
280 LASSERT(first_pit <= cur.lastpit(), /**/);
281 LASSERT(last_pit <= cur.lastpit(), /**/);
283 doRecordUndo(kind, cur, first_pit, last_pit, cur,
286 undo_finished_ = false;
288 //lyxerr << "undostack:\n";
289 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
290 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
294 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
296 undo_finished_ = true;
298 UndoElementStack & stack = isUndoOperation ? undostack_ : redostack_;
304 UndoElementStack & otherstack = isUndoOperation ? redostack_ : undostack_;
306 // Adjust undo stack and get hold of current undo data.
307 UndoElement undo = stack.top();
310 // We will store in otherstack the part of the document under 'undo'
311 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
313 doRecordUndo(ATOMIC_UNDO, cell_dit,
314 undo.from, cell_dit.lastpit() - undo.end, cur,
315 undo.isFullBuffer, !isUndoOperation);
317 // This does the actual undo/redo.
318 //LYXERR0("undo, performing: " << undo);
319 bool labelsUpdateNeeded = false;
320 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
321 if (undo.isFullBuffer) {
322 LASSERT(undo.pars, /**/);
323 // This is a full document
324 otherstack.top().bparams = buffer_.params();
325 buffer_.params() = undo.bparams;
326 swap(buffer_.paragraphs(), *undo.pars);
329 } else if (dit.inMathed()) {
330 // We stored the full cell here as there is not much to be
331 // gained by storing just 'a few' paragraphs (most if not
332 // all math inset cells have just one paragraph!)
333 //LYXERR0("undo.array: " << *undo.array);
334 LASSERT(undo.array, /**/);
335 dit.cell().swap(*undo.array);
339 // Some finer machinery is needed here.
340 Text * text = dit.text();
342 LASSERT(undo.pars, /**/);
343 ParagraphList & plist = text->paragraphs();
345 // remove new stuff between first and last
346 ParagraphList::iterator first = plist.begin();
347 advance(first, undo.from);
348 ParagraphList::iterator last = plist.begin();
349 advance(last, plist.size() - undo.end);
350 plist.erase(first, last);
352 // re-insert old stuff instead
353 first = plist.begin();
354 advance(first, undo.from);
356 // this ugly stuff is needed until we get rid of the
357 // inset_owner backpointer
358 ParagraphList::iterator pit = undo.pars->begin();
359 ParagraphList::iterator const end = undo.pars->end();
360 for (; pit != end; ++pit)
361 pit->setInsetOwner(dit.realInset());
362 plist.insert(first, undo.pars->begin(), undo.pars->end());
365 labelsUpdateNeeded = true;
367 LASSERT(undo.pars == 0, /**/);
368 LASSERT(undo.array == 0, /**/);
370 cur = undo.cursor.asDocIterator(&buffer_.inset());
372 if (labelsUpdateNeeded)
373 updateLabels(buffer_);
374 undo_finished_ = true;
379 void Undo::finishUndo()
381 // Make sure the next operation will be stored.
382 d->undo_finished_ = true;
386 bool Undo::textUndo(DocIterator & cur)
388 return d->textUndoOrRedo(cur, true);
392 bool Undo::textRedo(DocIterator & cur)
394 return d->textUndoOrRedo(cur, false);
398 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
400 d->recordUndo(kind, cur, cur.pit(), cur.pit());
404 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
408 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
412 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
414 d->recordUndo(kind, cur, cur.pit(), from);
418 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
419 pit_type from, pit_type to)
421 d->recordUndo(kind, cur, from, to);
425 void Undo::recordUndoFullDocument(DocIterator & cur)
429 doc_iterator_begin(d->buffer_.inset()),
430 0, d->buffer_.paragraphs().size() - 1,