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"
24 #include "DocIterator.h"
25 #include "Paragraph.h"
26 #include "ParagraphList.h"
29 #include "mathed/MathSupport.h"
30 #include "mathed/MathData.h"
32 #include "insets/Inset.h"
43 These are the elements put on the undo stack. Each object contains complete
44 paragraphs from some cell and sufficient information to restore the cursor
47 The cell is given by a DocIterator pointing to this cell, the 'interesting'
48 range of paragraphs by counting them from begin and end of cell,
51 The cursor is also given as DocIterator and should point to some place in
52 the stored paragraph range. In case of math, we simply store the whole
53 cell, as there usually is just a simple paragraph in a cell.
55 The idea is to store the contents of 'interesting' paragraphs in some
56 structure ('Undo') _before_ it is changed in some edit operation.
57 Obviously, the stored ranged should be as small as possible. However, it
58 there is a lower limit: The StableDocIterator pointing stored in the undo
59 class must be valid after the changes, too, as it will used as a pointer
60 where to insert the stored bits when performining undo.
65 /// Which kind of operation are we recording for?
67 /// the position of the cursor
68 StableDocIterator cursor;
69 /// the position of the cell described
70 StableDocIterator cell;
71 /// counted from begin of cell
73 /// complement to end of this cell
75 /// the contents of the saved Paragraphs (for texted)
77 /// the contents of the saved MathData (for mathed)
79 /// Only used in case of full backups
81 /// Only used in case of full backups
88 Private(Buffer & buffer): buffer_(buffer) {}
90 // Returns false if no undo possible.
91 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
93 void doRecordUndo(UndoKind kind,
94 DocIterator const & cell,
97 DocIterator const & cur,
99 bool isUndoOperation);
102 void recordUndo(UndoKind kind,
110 limited_stack<UndoElement> undostack;
112 limited_stack<UndoElement> redostack;
114 /// The flag used by Undo::finishUndo().
119 Undo::Undo(Buffer & buffer): d(new Undo::Private(buffer))
130 bool Undo::hasUndoStack() const
132 return !d->undostack.empty();
136 bool Undo::hasRedoStack() const
138 return !d->redostack.empty();
146 std::ostream & operator<<(std::ostream & os, UndoElement const & undo)
148 return os << " from: " << undo.from << " end: " << undo.end
149 << " cell:\n" << undo.cell
150 << " cursor:\n" << undo.cursor;
154 bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
156 StableDocIterator tmpi2 = i2;
157 tmpi2.pos() = i1.pos();
164 void Undo::Private::doRecordUndo(UndoKind kind,
165 DocIterator const & cell,
166 pit_type first_pit, pit_type last_pit,
167 DocIterator const & cur,
169 bool isUndoOperation)
171 if (first_pit > last_pit)
172 std::swap(first_pit, last_pit);
173 // create the position information of the Undo entry
180 undo.bparams = buffer_.params();
181 undo.isFullBuffer = isFullBuffer;
182 //lyxerr << "recordUndo: cur: " << cur << endl;
183 //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
184 //lyxerr << "recordUndo: cell: " << cell << endl;
185 undo.from = first_pit;
186 undo.end = cell.lastpit() - last_pit;
188 limited_stack<UndoElement> & stack = isUndoOperation ?
189 undostack : redostack;
191 // Undo::ATOMIC are always recorded (no overlapping there).
192 // As nobody wants all removed character appear one by one when undoing,
193 // we want combine 'similar' non-ATOMIC undo recordings to one.
195 && kind != ATOMIC_UNDO
197 && samePar(stack.top().cell, undo.cell)
198 && stack.top().kind == undo.kind
199 && stack.top().from == undo.from
200 && stack.top().end == undo.end)
203 // fill in the real data to be saved
204 if (cell.inMathed()) {
205 // simply use the whole cell
206 undo.array = new MathData(cell.cell());
208 // some more effort needed here as 'the whole cell' of the
209 // main Text _is_ the whole document.
210 // record the relevant paragraphs
211 Text const * text = cell.text();
213 ParagraphList const & plist = text->paragraphs();
214 ParagraphList::const_iterator first = plist.begin();
215 advance(first, first_pit);
216 ParagraphList::const_iterator last = plist.begin();
217 advance(last, last_pit + 1);
218 undo.pars = new ParagraphList(first, last);
221 // push the undo entry to undo stack
223 //lyxerr << "undo record: " << stack.top() << std::endl;
225 // next time we'll try again to combine entries if possible
226 undo_finished = false;
230 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
231 pit_type first_pit, pit_type last_pit)
233 BOOST_ASSERT(first_pit <= cur.lastpit());
234 BOOST_ASSERT(last_pit <= cur.lastpit());
236 doRecordUndo(kind, cur, first_pit, last_pit, cur,
239 undo_finished = false;
241 //lyxerr << "undostack:\n";
242 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
243 // lyxerr << " " << i << ": " << buf.undostack()[i] << std::endl;
247 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
249 undo_finished = true;
251 limited_stack<UndoElement> & stack = isUndoOperation ?
252 undostack : redostack;
258 limited_stack<UndoElement> & otherstack = isUndoOperation ?
259 redostack : undostack;
261 // Adjust undo stack and get hold of current undo data.
262 UndoElement undo = stack.top();
265 // We will store in otherstack the part of the document under 'undo'
266 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
268 doRecordUndo(ATOMIC_UNDO, cell_dit,
269 undo.from, cell_dit.lastpit() - undo.end, cur,
270 undo.isFullBuffer, !isUndoOperation);
272 // This does the actual undo/redo.
273 //lyxerr << "undo, performing: " << undo << std::endl;
274 bool labelsUpdateNeeded = false;
275 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
276 if (undo.isFullBuffer) {
277 BOOST_ASSERT(undo.pars);
278 // This is a full document
279 otherstack.top().bparams = buffer_.params();
280 buffer_.params() = undo.bparams;
281 std::swap(buffer_.paragraphs(), *undo.pars);
284 } else if (dit.inMathed()) {
285 // We stored the full cell here as there is not much to be
286 // gained by storing just 'a few' paragraphs (most if not
287 // all math inset cells have just one paragraph!)
288 //lyxerr << "undo.array: " << *undo.array <<endl;
289 BOOST_ASSERT(undo.array);
290 dit.cell().swap(*undo.array);
294 // Some finer machinery is needed here.
295 Text * text = dit.text();
297 BOOST_ASSERT(undo.pars);
298 ParagraphList & plist = text->paragraphs();
300 // remove new stuff between first and last
301 ParagraphList::iterator first = plist.begin();
302 advance(first, undo.from);
303 ParagraphList::iterator last = plist.begin();
304 advance(last, plist.size() - undo.end);
305 plist.erase(first, last);
307 // re-insert old stuff instead
308 first = plist.begin();
309 advance(first, undo.from);
311 // this ugly stuff is needed until we get rid of the
312 // inset_owner backpointer
313 ParagraphList::iterator pit = undo.pars->begin();
314 ParagraphList::iterator const end = undo.pars->end();
315 for (; pit != end; ++pit)
316 pit->setInsetOwner(dit.realInset());
317 plist.insert(first, undo.pars->begin(), undo.pars->end());
320 labelsUpdateNeeded = true;
322 BOOST_ASSERT(undo.pars == 0);
323 BOOST_ASSERT(undo.array == 0);
325 cur = undo.cursor.asDocIterator(&buffer_.inset());
327 if (labelsUpdateNeeded)
328 updateLabels(buffer_);
329 undo_finished = true;
334 void Undo::finishUndo()
336 // Make sure the next operation will be stored.
337 d->undo_finished = true;
341 bool Undo::textUndo(DocIterator & cur)
343 return d->textUndoOrRedo(cur, true);
347 bool Undo::textRedo(DocIterator & cur)
349 return d->textUndoOrRedo(cur, false);
353 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
355 d->recordUndo(kind, cur, cur.pit(), cur.pit());
359 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
363 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
367 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
369 d->recordUndo(kind, cur, cur.pit(), from);
373 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
374 pit_type from, pit_type to)
376 d->recordUndo(kind, cur, from, to);
380 void Undo::recordUndoFullDocument(DocIterator & cur)
384 doc_iterator_begin(d->buffer_.inset()),
385 0, d->buffer_.paragraphs().size() - 1,