]> git.lyx.org Git - lyx.git/blob - src/Undo.cpp
this we don't need anymore
[lyx.git] / src / Undo.cpp
1 /**
2  * \file Undo.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author John Levon
9  * \author André Pönitz
10  * \author Jürgen Vigna
11  * \author Abdelrazak Younes
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "Undo.h"
19
20 #include "Buffer.h"
21 #include "BufferParams.h"
22 #include "buffer_funcs.h"
23 #include "DocIterator.h"
24 #include "Paragraph.h"
25 #include "ParagraphList.h"
26 #include "Text.h"
27
28 #include "mathed/MathSupport.h"
29 #include "mathed/MathData.h"
30
31 #include "insets/Inset.h"
32
33 #include "support/assert.h"
34 #include "support/debug.h"
35
36 #include <algorithm>
37 #include <deque>
38 #include <ostream>
39
40 using namespace std;
41 using namespace lyx::support;
42
43
44 namespace lyx {
45
46 /**
47 These are the elements put on the undo stack. Each object contains complete
48 paragraphs from some cell and sufficient information to restore the cursor
49 state.
50
51 The cell is given by a DocIterator pointing to this cell, the 'interesting'
52 range of paragraphs by counting them from begin and end of cell,
53 respectively.
54
55 The cursor is also given as DocIterator and should point to some place in
56 the stored paragraph range.  In case of math, we simply store the whole
57 cell, as there usually is just a simple paragraph in a cell.
58
59 The idea is to store the contents of 'interesting' paragraphs in some
60 structure ('Undo') _before_ it is changed in some edit operation.
61 Obviously, the stored ranged should be as small as possible. However, it
62 there is a lower limit: The StableDocIterator pointing stored in the undo
63 class must be valid after the changes, too, as it will used as a pointer
64 where to insert the stored bits when performining undo.
65 */
66
67
68 struct UndoElement
69 {
70         /// Which kind of operation are we recording for?
71         UndoKind kind;
72         /// the position of the cursor
73         StableDocIterator cursor;
74         /// the position of the cell described
75         StableDocIterator cell;
76         /// counted from begin of cell
77         pit_type from;
78         /// complement to end of this cell
79         pit_type end;
80         /// the contents of the saved Paragraphs (for texted)
81         ParagraphList * pars;
82         /// the contents of the saved MathData (for mathed)
83         MathData * array;
84         /// Only used in case of full backups
85         BufferParams bparams;
86         /// Only used in case of full backups
87         bool isFullBuffer;
88 };
89
90
91 class UndoElementStack 
92 {
93 public:
94         /// limit is the maximum size of the stack
95         UndoElementStack(size_t limit = 100) { limit_ = limit; }
96         /// limit is the maximum size of the stack
97         ~UndoElementStack() { clear(); }
98
99         /// Return the top element.
100         UndoElement & top() { return c_.front(); }
101
102         /// Pop and throw away the top element.
103         void pop() { c_.pop_front(); }
104
105         /// Return true if the stack is empty.
106         bool empty() const { return c_.empty(); }
107
108         /// Clear all elements, deleting them.
109         void clear() {
110                 for (size_t i = 0; i != c_.size(); ++i) {
111                         delete c_[i].array;
112                         delete c_[i].pars;
113                 }
114                 c_.clear();
115         }
116
117         /// Push an item on to the stack, deleting the
118         /// bottom item on overflow.
119         void push(UndoElement const & v) {
120                 c_.push_front(v);
121                 if (c_.size() > limit_)
122                         c_.pop_back();
123         }
124
125 private:
126         /// Internal contents.
127         std::deque<UndoElement> c_;
128         /// The maximum number elements stored.
129         size_t limit_;
130 };
131
132
133 struct Undo::Private
134 {
135         Private(Buffer & buffer) : buffer_(buffer), undo_finished_(true) {}
136         
137         // Returns false if no undo possible.
138         bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
139         ///
140         void doRecordUndo(UndoKind kind,
141                 DocIterator const & cell,
142                 pit_type first_pit,
143                 pit_type last_pit,
144                 DocIterator const & cur,
145                 bool isFullBuffer,
146                 bool isUndoOperation);
147
148         ///
149         void recordUndo(UndoKind kind,
150                 DocIterator & cur,
151                 pit_type first_pit,
152                 pit_type last_pit);
153
154         ///
155         Buffer & buffer_;
156         /// Undo stack.
157         UndoElementStack undostack_;
158         /// Redo stack.
159         UndoElementStack redostack_;
160
161         /// The flag used by Undo::finishUndo().
162         bool undo_finished_;
163 };
164
165
166 /////////////////////////////////////////////////////////////////////
167 //
168 // Undo
169 //
170 /////////////////////////////////////////////////////////////////////
171
172
173 Undo::Undo(Buffer & buffer)
174         : d(new Undo::Private(buffer))
175 {}
176
177
178 Undo::~Undo()
179 {
180         delete d;
181 }
182
183
184 bool Undo::hasUndoStack() const
185 {
186         return !d->undostack_.empty();
187 }
188
189
190 bool Undo::hasRedoStack() const
191 {
192         return !d->redostack_.empty();
193 }
194
195
196 #if 0
197 static ostream & operator<<(ostream & os, UndoElement const & undo)
198 {
199         return os << " from: " << undo.from << " end: " << undo.end
200                 << " cell:\n" << undo.cell
201                 << " cursor:\n" << undo.cursor;
202 }
203 #endif
204
205
206 static bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
207 {
208         StableDocIterator tmpi2 = i2;
209         tmpi2.pos() = i1.pos();
210         return i1 == tmpi2;
211 }
212
213
214 /////////////////////////////////////////////////////////////////////
215 //
216 // Undo::Private
217 //
218 ///////////////////////////////////////////////////////////////////////
219
220 void Undo::Private::doRecordUndo(UndoKind kind,
221         DocIterator const & cell,
222         pit_type first_pit, pit_type last_pit,
223         DocIterator const & cur,
224         bool isFullBuffer,
225         bool isUndoOperation)
226 {
227         if (first_pit > last_pit)
228                 swap(first_pit, last_pit);
229         // create the position information of the Undo entry
230         UndoElement undo;
231         undo.array = 0;
232         undo.pars = 0;
233         undo.kind = kind;
234         undo.cell = cell;
235         undo.cursor = cur;
236         undo.bparams = buffer_.params();
237         undo.isFullBuffer = isFullBuffer;
238         //lyxerr << "recordUndo: cur: " << cur << endl;
239         //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
240         //lyxerr << "recordUndo: cell: " << cell << endl;
241         undo.from = first_pit;
242         undo.end = cell.lastpit() - last_pit;
243
244         UndoElementStack & stack = isUndoOperation ?  undostack_ : redostack_;
245
246         // Undo::ATOMIC are always recorded (no overlapping there).
247         // As nobody wants all removed character appear one by one when undoing,
248         // we want combine 'similar' non-ATOMIC undo recordings to one.
249         if (!undo_finished_
250             && kind != ATOMIC_UNDO
251             && !stack.empty()
252             && samePar(stack.top().cell, undo.cell)
253             && stack.top().kind == undo.kind
254             && stack.top().from == undo.from
255             && stack.top().end == undo.end)
256                 return;
257
258         // fill in the real data to be saved
259         if (cell.inMathed()) {
260                 // simply use the whole cell
261                 undo.array = new MathData(cell.cell());
262         } else {
263                 // some more effort needed here as 'the whole cell' of the
264                 // main Text _is_ the whole document.
265                 // record the relevant paragraphs
266                 Text const * text = cell.text();
267                 LASSERT(text, /**/);
268                 ParagraphList const & plist = text->paragraphs();
269                 ParagraphList::const_iterator first = plist.begin();
270                 advance(first, first_pit);
271                 ParagraphList::const_iterator last = plist.begin();
272                 advance(last, last_pit + 1);
273                 undo.pars = new ParagraphList(first, last);
274         }
275
276         // push the undo entry to undo stack
277         stack.push(undo);
278         //lyxerr << "undo record: " << stack.top() << endl;
279
280         // next time we'll try again to combine entries if possible
281         undo_finished_ = false;
282 }
283
284
285 void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
286         pit_type first_pit, pit_type last_pit)
287 {
288         LASSERT(first_pit <= cur.lastpit(), /**/);
289         LASSERT(last_pit <= cur.lastpit(), /**/);
290
291         doRecordUndo(kind, cur, first_pit, last_pit, cur,
292                 false, true);
293
294         undo_finished_ = false;
295         redostack_.clear();
296         //lyxerr << "undostack:\n";
297         //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
298         //      lyxerr << "  " << i << ": " << buf.undostack()[i] << endl;
299 }
300
301
302 bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
303 {
304         undo_finished_ = true;
305
306         UndoElementStack & stack = isUndoOperation ?  undostack_ : redostack_;
307
308         if (stack.empty())
309                 // Nothing to do.
310                 return false;
311
312         UndoElementStack & otherstack = isUndoOperation ?  redostack_ : undostack_;
313
314         // Adjust undo stack and get hold of current undo data.
315         UndoElement undo = stack.top();
316         stack.pop();
317
318         // We will store in otherstack the part of the document under 'undo'
319         DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
320
321         doRecordUndo(ATOMIC_UNDO, cell_dit,
322                 undo.from, cell_dit.lastpit() - undo.end, cur,
323                 undo.isFullBuffer, !isUndoOperation);
324
325         // This does the actual undo/redo.
326         //LYXERR0("undo, performing: " << undo);
327         bool labelsUpdateNeeded = false;
328         DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
329         if (undo.isFullBuffer) {
330                 LASSERT(undo.pars, /**/);
331                 // This is a full document
332                 otherstack.top().bparams = buffer_.params();
333                 buffer_.params() = undo.bparams;
334                 swap(buffer_.paragraphs(), *undo.pars);
335                 delete undo.pars;
336                 undo.pars = 0;
337         } else if (dit.inMathed()) {
338                 // We stored the full cell here as there is not much to be
339                 // gained by storing just 'a few' paragraphs (most if not
340                 // all math inset cells have just one paragraph!)
341                 //LYXERR0("undo.array: " << *undo.array);
342                 LASSERT(undo.array, /**/);
343                 dit.cell().swap(*undo.array);
344                 delete undo.array;
345                 undo.array = 0;
346         } else {
347                 // Some finer machinery is needed here.
348                 Text * text = dit.text();
349                 LASSERT(text, /**/);
350                 LASSERT(undo.pars, /**/);
351                 ParagraphList & plist = text->paragraphs();
352
353                 // remove new stuff between first and last
354                 ParagraphList::iterator first = plist.begin();
355                 advance(first, undo.from);
356                 ParagraphList::iterator last = plist.begin();
357                 advance(last, plist.size() - undo.end);
358                 plist.erase(first, last);
359
360                 // re-insert old stuff instead
361                 first = plist.begin();
362                 advance(first, undo.from);
363
364                 // this ugly stuff is needed until we get rid of the
365                 // inset_owner backpointer
366                 ParagraphList::iterator pit = undo.pars->begin();
367                 ParagraphList::iterator const end = undo.pars->end();
368                 for (; pit != end; ++pit)
369                         pit->setInsetOwner(dit.realInset());
370                 plist.insert(first, undo.pars->begin(), undo.pars->end());
371                 delete undo.pars;
372                 undo.pars = 0;
373                 labelsUpdateNeeded = true;
374         }
375         LASSERT(undo.pars == 0, /**/);
376         LASSERT(undo.array == 0, /**/);
377
378         cur = undo.cursor.asDocIterator(&buffer_.inset());
379         
380         if (labelsUpdateNeeded)
381                 updateLabels(buffer_);
382         undo_finished_ = true;
383         return true;
384 }
385
386
387 void Undo::finishUndo()
388 {
389         // Make sure the next operation will be stored.
390         d->undo_finished_ = true;
391 }
392
393
394 bool Undo::textUndo(DocIterator & cur)
395 {
396         return d->textUndoOrRedo(cur, true);
397 }
398
399
400 bool Undo::textRedo(DocIterator & cur)
401 {
402         return d->textUndoOrRedo(cur, false);
403 }
404
405
406 void Undo::recordUndo(DocIterator & cur, UndoKind kind)
407 {
408         d->recordUndo(kind, cur, cur.pit(), cur.pit());
409 }
410
411
412 void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
413 {
414         DocIterator c = cur;
415         c.pop_back();
416         d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
417 }
418
419
420 void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
421 {
422         d->recordUndo(kind, cur, cur.pit(), from);
423 }
424
425
426 void Undo::recordUndo(DocIterator & cur, UndoKind kind,
427         pit_type from, pit_type to)
428 {
429         d->recordUndo(kind, cur, from, to);
430 }
431
432
433 void Undo::recordUndoFullDocument(DocIterator & cur)
434 {
435         d->doRecordUndo(
436                 ATOMIC_UNDO,
437                 doc_iterator_begin(d->buffer_.inset()),
438                 0, d->buffer_.paragraphs().size() - 1,
439                 cur,
440                 true,
441                 true
442         );
443 }
444
445
446 } // namespace lyx