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"
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.
68 /// Which kind of operation are we recording for?
70 /// the position of the cursor
71 StableDocIterator cursor;
72 /// the position of the cell described
73 StableDocIterator cell;
74 /// counted from begin of cell
76 /// complement to end of this cell
78 /// the contents of the saved Paragraphs (for texted)
80 /// the contents of the saved MathData (for mathed)
82 /// Only used in case of full backups
84 /// Only used in case of full backups
91 Private(Buffer & buffer): buffer_(buffer) {}
93 // Returns false if no undo possible.
94 bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
96 void doRecordUndo(UndoKind kind,
97 DocIterator const & cell,
100 DocIterator const & cur,
102 bool isUndoOperation);
105 void recordUndo(UndoKind kind,
113 limited_stack<UndoElement> undostack;
115 limited_stack<UndoElement> redostack;
117 /// The flag used by Undo::finishUndo().
122 Undo::Undo(Buffer & buffer): d(new Undo::Private(buffer))
133 bool Undo::hasUndoStack() const
135 return !d->undostack.empty();
139 bool Undo::hasRedoStack() const
141 return !d->redostack.empty();
149 ostream & operator<<(ostream & os, UndoElement const & undo)
151 return os << " from: " << undo.from << " end: " << undo.end
152 << " cell:\n" << undo.cell
153 << " cursor:\n" << undo.cursor;
157 bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
159 StableDocIterator tmpi2 = i2;
160 tmpi2.pos() = i1.pos();
167 void Undo::Private::doRecordUndo(UndoKind kind,
168 DocIterator const & cell,
169 pit_type first_pit, pit_type last_pit,
170 DocIterator const & cur,
172 bool isUndoOperation)
174 if (first_pit > last_pit)
175 swap(first_pit, last_pit);
176 // create the position information of the Undo entry
183 undo.bparams = buffer_.params();
184 undo.isFullBuffer = isFullBuffer;
185 //lyxerr << "recordUndo: cur: " << cur << endl;
186 //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
187 //lyxerr << "recordUndo: cell: " << cell << endl;
188 undo.from = first_pit;
189 undo.end = cell.lastpit() - last_pit;
191 limited_stack<UndoElement> & stack = isUndoOperation ?
192 undostack : redostack;
194 // Undo::ATOMIC are always recorded (no overlapping there).
195 // As nobody wants all removed character appear one by one when undoing,
196 // we want combine 'similar' non-ATOMIC undo recordings to one.
198 && kind != ATOMIC_UNDO
200 && samePar(stack.top().cell, undo.cell)
201 && stack.top().kind == undo.kind
202 && stack.top().from == undo.from
203 && stack.top().end == undo.end)
206 // fill in the real data to be saved
207 if (cell.inMathed()) {
208 // simply use the whole cell
209 undo.array = new MathData(cell.cell());
211 // some more effort needed here as 'the whole cell' of the
212 // main Text _is_ the whole document.
213 // record the relevant paragraphs
214 Text const * text = cell.text();
216 ParagraphList const & plist = text->paragraphs();
217 ParagraphList::const_iterator first = plist.begin();
218 advance(first, first_pit);
219 ParagraphList::const_iterator last = plist.begin();
220 advance(last, last_pit + 1);
221 undo.pars = new ParagraphList(first, last);
224 // push the undo entry to undo stack
226 //lyxerr << "undo record: " << stack.top() << endl;
228 // next time we'll try again to combine entries if possible
229 undo_finished = false;
233 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
234 pit_type first_pit, pit_type last_pit)
236 BOOST_ASSERT(first_pit <= cur.lastpit());
237 BOOST_ASSERT(last_pit <= cur.lastpit());
239 doRecordUndo(kind, cur, first_pit, last_pit, cur,
242 undo_finished = false;
244 //lyxerr << "undostack:\n";
245 //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
246 // lyxerr << " " << i << ": " << buf.undostack()[i] << endl;
250 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
252 undo_finished = true;
254 limited_stack<UndoElement> & stack = isUndoOperation ?
255 undostack : redostack;
261 limited_stack<UndoElement> & otherstack = isUndoOperation ?
262 redostack : undostack;
264 // Adjust undo stack and get hold of current undo data.
265 UndoElement undo = stack.top();
268 // We will store in otherstack the part of the document under 'undo'
269 DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
271 doRecordUndo(ATOMIC_UNDO, cell_dit,
272 undo.from, cell_dit.lastpit() - undo.end, cur,
273 undo.isFullBuffer, !isUndoOperation);
275 // This does the actual undo/redo.
276 //LYXERR0("undo, performing: " << undo);
277 bool labelsUpdateNeeded = false;
278 DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
279 if (undo.isFullBuffer) {
280 BOOST_ASSERT(undo.pars);
281 // This is a full document
282 otherstack.top().bparams = buffer_.params();
283 buffer_.params() = undo.bparams;
284 swap(buffer_.paragraphs(), *undo.pars);
287 } else if (dit.inMathed()) {
288 // We stored the full cell here as there is not much to be
289 // gained by storing just 'a few' paragraphs (most if not
290 // all math inset cells have just one paragraph!)
291 //LYXERR0("undo.array: " << *undo.array);
292 BOOST_ASSERT(undo.array);
293 dit.cell().swap(*undo.array);
297 // Some finer machinery is needed here.
298 Text * text = dit.text();
300 BOOST_ASSERT(undo.pars);
301 ParagraphList & plist = text->paragraphs();
303 // remove new stuff between first and last
304 ParagraphList::iterator first = plist.begin();
305 advance(first, undo.from);
306 ParagraphList::iterator last = plist.begin();
307 advance(last, plist.size() - undo.end);
308 plist.erase(first, last);
310 // re-insert old stuff instead
311 first = plist.begin();
312 advance(first, undo.from);
314 // this ugly stuff is needed until we get rid of the
315 // inset_owner backpointer
316 ParagraphList::iterator pit = undo.pars->begin();
317 ParagraphList::iterator const end = undo.pars->end();
318 for (; pit != end; ++pit)
319 pit->setInsetOwner(dit.realInset());
320 plist.insert(first, undo.pars->begin(), undo.pars->end());
323 labelsUpdateNeeded = true;
325 BOOST_ASSERT(undo.pars == 0);
326 BOOST_ASSERT(undo.array == 0);
328 cur = undo.cursor.asDocIterator(&buffer_.inset());
330 if (labelsUpdateNeeded)
331 updateLabels(buffer_);
332 undo_finished = true;
337 void Undo::finishUndo()
339 // Make sure the next operation will be stored.
340 d->undo_finished = true;
344 bool Undo::textUndo(DocIterator & cur)
346 return d->textUndoOrRedo(cur, true);
350 bool Undo::textRedo(DocIterator & cur)
352 return d->textUndoOrRedo(cur, false);
356 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
358 d->recordUndo(kind, cur, cur.pit(), cur.pit());
362 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
366 d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
370 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
372 d->recordUndo(kind, cur, cur.pit(), from);
376 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
377 pit_type from, pit_type to)
379 d->recordUndo(kind, cur, from, to);
383 void Undo::recordUndoFullDocument(DocIterator & cur)
387 doc_iterator_begin(d->buffer_.inset()),
388 0, d->buffer_.paragraphs().size() - 1,