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/debug.h"
34 #include "support/limited_stack.h"
36 #include "support/assert.h"
42 using namespace lyx::support;
48 These are the elements put on the undo stack. Each object contains complete
49 paragraphs from some cell and sufficient information to restore the cursor
52 The cell is given by a DocIterator pointing to this cell, the 'interesting'
53 range of paragraphs by counting them from begin and end of cell,
56 The cursor is also given as DocIterator and should point to some place in
57 the stored paragraph range. In case of math, we simply store the whole
58 cell, as there usually is just a simple paragraph in a cell.
60 The idea is to store the contents of 'interesting' paragraphs in some
61 structure ('Undo') _before_ it is changed in some edit operation.
62 Obviously, the stored ranged should be as small as possible. However, it
63 there is a lower limit: The StableDocIterator pointing stored in the undo
64 class must be valid after the changes, too, as it will used as a pointer
65 where to insert the stored bits when performining undo.
71 /// Which kind of operation are we recording for?
73 /// the position of the cursor
74 StableDocIterator cursor;
75 /// the position of the cell described
76 StableDocIterator cell;
77 /// counted from begin of cell
79 /// complement to end of this cell
81 /// the contents of the saved Paragraphs (for texted)
83 /// the contents of the saved MathData (for mathed)
85 /// Only used in case of full backups
87 /// Only used in case of full backups
94 Private(Buffer & buffer) : buffer_(buffer) {}
96 // Returns false if no undo possible.
97 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
99 void doRecordUndo(UndoKind kind,
100 DocIterator const & cell,
103 DocIterator const & cur,
105 bool isUndoOperation);
108 void recordUndo(UndoKind kind,
116 limited_stack<UndoElement> undostack;
118 limited_stack<UndoElement> redostack;
120 /// The flag used by Undo::finishUndo().
125 /////////////////////////////////////////////////////////////////////
129 /////////////////////////////////////////////////////////////////////
132 Undo::Undo(Buffer & buffer)
133 : d(new Undo::Private(buffer))
143 bool Undo::hasUndoStack() const
145 return !d->undostack.empty();
149 bool Undo::hasRedoStack() const
151 return !d->redostack.empty();
155 static ostream & operator<<(ostream & os, UndoElement const & undo)
157 return os << " from: " << undo.from << " end: " << undo.end
158 << " cell:\n" << undo.cell
159 << " cursor:\n" << undo.cursor;
163 static bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
165 StableDocIterator tmpi2 = i2;
166 tmpi2.pos() = i1.pos();
171 /////////////////////////////////////////////////////////////////////
175 ///////////////////////////////////////////////////////////////////////
177 void Undo::Private::doRecordUndo(UndoKind kind,
178 DocIterator const & cell,
179 pit_type first_pit, pit_type last_pit,
180 DocIterator const & cur,
182 bool isUndoOperation)
184 if (first_pit > last_pit)
185 swap(first_pit, last_pit);
186 // create the position information of the Undo entry
193 undo.bparams = buffer_.params();
194 undo.isFullBuffer = isFullBuffer;
195 //lyxerr << "recordUndo: cur: " << cur << endl;
196 //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
197 //lyxerr << "recordUndo: cell: " << cell << endl;
198 undo.from = first_pit;
199 undo.end = cell.lastpit() - last_pit;
201 limited_stack<UndoElement> & stack = isUndoOperation ?
202 undostack : redostack;
204 // Undo::ATOMIC are always recorded (no overlapping there).
205 // As nobody wants all removed character appear one by one when undoing,
206 // we want combine 'similar' non-ATOMIC undo recordings to one.
208 && kind != ATOMIC_UNDO
210 && samePar(stack.top().cell, undo.cell)
211 && stack.top().kind == undo.kind
212 && stack.top().from == undo.from
213 && stack.top().end == undo.end)
216 // fill in the real data to be saved
217 if (cell.inMathed()) {
218 // simply use the whole cell
219 undo.array = new MathData(cell.cell());
221 // some more effort needed here as 'the whole cell' of the
222 // main Text _is_ the whole document.
223 // record the relevant paragraphs
224 Text const * text = cell.text();
226 ParagraphList const & plist = text->paragraphs();
227 ParagraphList::const_iterator first = plist.begin();
228 advance(first, first_pit);
229 ParagraphList::const_iterator last = plist.begin();
230 advance(last, last_pit + 1);
231 undo.pars = new ParagraphList(first, last);
234 // push the undo entry to undo stack
236 //lyxerr << "undo record: " << stack.top() << endl;
238 // next time we'll try again to combine entries if possible
239 undo_finished = false;
243 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
244 pit_type first_pit, pit_type last_pit)
246 LASSERT(first_pit <= cur.lastpit(), /**/);
247 LASSERT(last_pit <= cur.lastpit(), /**/);
249 doRecordUndo(kind, cur, first_pit, last_pit, cur,
252 undo_finished = false;
254 //lyxerr << "undostack:\n";
255 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
256 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
260 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
262 undo_finished = true;
264 limited_stack<UndoElement> & stack = isUndoOperation ?
265 undostack : redostack;
271 limited_stack<UndoElement> & otherstack = isUndoOperation ?
272 redostack : undostack;
274 // Adjust undo stack and get hold of current undo data.
275 UndoElement undo = stack.top();
278 // We will store in otherstack the part of the document under 'undo'
279 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
281 doRecordUndo(ATOMIC_UNDO, cell_dit,
282 undo.from, cell_dit.lastpit() - undo.end, cur,
283 undo.isFullBuffer, !isUndoOperation);
285 // This does the actual undo/redo.
286 //LYXERR0("undo, performing: " << undo);
287 bool labelsUpdateNeeded = false;
288 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
289 if (undo.isFullBuffer) {
290 LASSERT(undo.pars, /**/);
291 // This is a full document
292 otherstack.top().bparams = buffer_.params();
293 buffer_.params() = undo.bparams;
294 swap(buffer_.paragraphs(), *undo.pars);
297 } else if (dit.inMathed()) {
298 // We stored the full cell here as there is not much to be
299 // gained by storing just 'a few' paragraphs (most if not
300 // all math inset cells have just one paragraph!)
301 //LYXERR0("undo.array: " << *undo.array);
302 LASSERT(undo.array, /**/);
303 dit.cell().swap(*undo.array);
307 // Some finer machinery is needed here.
308 Text * text = dit.text();
310 LASSERT(undo.pars, /**/);
311 ParagraphList & plist = text->paragraphs();
313 // remove new stuff between first and last
314 ParagraphList::iterator first = plist.begin();
315 advance(first, undo.from);
316 ParagraphList::iterator last = plist.begin();
317 advance(last, plist.size() - undo.end);
318 plist.erase(first, last);
320 // re-insert old stuff instead
321 first = plist.begin();
322 advance(first, undo.from);
324 // this ugly stuff is needed until we get rid of the
325 // inset_owner backpointer
326 ParagraphList::iterator pit = undo.pars->begin();
327 ParagraphList::iterator const end = undo.pars->end();
328 for (; pit != end; ++pit)
329 pit->setInsetOwner(dit.realInset());
330 plist.insert(first, undo.pars->begin(), undo.pars->end());
333 labelsUpdateNeeded = true;
335 LASSERT(undo.pars == 0, /**/);
336 LASSERT(undo.array == 0, /**/);
338 cur = undo.cursor.asDocIterator(&buffer_.inset());
340 if (labelsUpdateNeeded)
341 updateLabels(buffer_);
342 undo_finished = true;
347 void Undo::finishUndo()
349 // Make sure the next operation will be stored.
350 d->undo_finished = true;
354 bool Undo::textUndo(DocIterator & cur)
356 return d->textUndoOrRedo(cur, true);
360 bool Undo::textRedo(DocIterator & cur)
362 return d->textUndoOrRedo(cur, false);
366 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
368 d->recordUndo(kind, cur, cur.pit(), cur.pit());
372 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
376 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
380 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
382 d->recordUndo(kind, cur, cur.pit(), from);
386 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
387 pit_type from, pit_type to)
389 d->recordUndo(kind, cur, from, to);
393 void Undo::recordUndoFullDocument(DocIterator & cur)
397 doc_iterator_begin(d->buffer_.inset()),
398 0, d->buffer_.paragraphs().size() - 1,