]> git.lyx.org Git - lyx.git/blob - src/Undo.cpp
* BufferView::buffer() returns a reference instead of a pointer.
[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  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include <config.h>
16
17 #include "Undo.h"
18
19 #include "Buffer.h"
20 #include "buffer_funcs.h"
21 #include "Cursor.h"
22 #include "debug.h"
23 #include "BufferView.h"
24 #include "Text.h"
25 #include "Paragraph.h"
26 #include "ParagraphList.h"
27
28 #include "mathed/MathSupport.h"
29 #include "mathed/MathData.h"
30
31 #include "insets/Inset.h"
32
33 #include <algorithm>
34
35
36 namespace lyx {
37
38 using std::advance;
39 using std::endl;
40
41
42 namespace {
43
44 /// The flag used by finishUndo().
45 bool undo_finished;
46
47
48 std::ostream & operator<<(std::ostream & os, Undo const & undo)
49 {
50         return os << " from: " << undo.from << " end: " << undo.end
51                 << " cell:\n" << undo.cell
52                 << " cursor:\n" << undo.cursor;
53 }
54
55
56 bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
57 {
58         StableDocIterator tmpi2 = i2;
59         tmpi2.pos() = i1.pos();
60         return i1 == tmpi2;
61 }
62
63
64 void doRecordUndo(Undo::undo_kind kind,
65         DocIterator const & cell,
66         pit_type first_pit, pit_type last_pit,
67         DocIterator const & cur,
68         BufferParams const & bparams,
69         bool isFullBuffer,
70         limited_stack<Undo> & stack)
71 {
72         if (first_pit > last_pit)
73                 std::swap(first_pit, last_pit);
74         // create the position information of the Undo entry
75         Undo undo;
76         undo.array = 0;
77         undo.pars = 0;
78         undo.kind = kind;
79         undo.cell = cell;
80         undo.cursor = cur;
81         undo.bparams = bparams ;
82         undo.isFullBuffer = isFullBuffer;
83         //lyxerr << "recordUndo: cur: " << cur << endl;
84         //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
85         //lyxerr << "recordUndo: cell: " << cell << endl;
86         undo.from = first_pit;
87         undo.end = cell.lastpit() - last_pit;
88
89         // Undo::ATOMIC are always recorded (no overlapping there).
90         // As nobody wants all removed character appear one by one when undoing,
91         // we want combine 'similar' non-ATOMIC undo recordings to one.
92         if (!undo_finished
93             && kind != Undo::ATOMIC
94             && !stack.empty()
95             && samePar(stack.top().cell, undo.cell)
96             && stack.top().kind == undo.kind
97             && stack.top().from == undo.from
98             && stack.top().end == undo.end)
99                 return;
100
101         // fill in the real data to be saved
102         if (cell.inMathed()) {
103                 // simply use the whole cell
104                 undo.array = new MathData(cell.cell());
105         } else {
106                 // some more effort needed here as 'the whole cell' of the
107                 // main Text _is_ the whole document.
108                 // record the relevant paragraphs
109                 Text const * text = cell.text();
110                 BOOST_ASSERT(text);
111                 ParagraphList const & plist = text->paragraphs();
112                 ParagraphList::const_iterator first = plist.begin();
113                 advance(first, first_pit);
114                 ParagraphList::const_iterator last = plist.begin();
115                 advance(last, last_pit + 1);
116                 undo.pars = new ParagraphList(first, last);
117         }
118
119         // push the undo entry to undo stack
120         stack.push(undo);
121         //lyxerr << "undo record: " << stack.top() << std::endl;
122
123         // next time we'll try again to combine entries if possible
124         undo_finished = false;
125 }
126
127
128 void recordUndo(Undo::undo_kind kind,
129         Cursor & cur, pit_type first_pit, pit_type last_pit,
130         limited_stack<Undo> & stack)
131 {
132         BOOST_ASSERT(first_pit <= cur.lastpit());
133         BOOST_ASSERT(last_pit <= cur.lastpit());
134
135         doRecordUndo(kind, cur, first_pit, last_pit, cur,
136                 cur.bv().buffer().params(), false, stack);
137 }
138
139
140
141 // Returns false if no undo possible.
142 bool textUndoOrRedo(BufferView & bv,
143         limited_stack<Undo> & stack, limited_stack<Undo> & otherstack)
144 {
145         finishUndo();
146
147         if (stack.empty()) {
148                 // Nothing to do.
149                 return false;
150         }
151
152         // Adjust undo stack and get hold of current undo data.
153         Undo undo = stack.top();
154         stack.pop();
155
156         // We will store in otherstack the part of the document under 'undo'
157         Buffer & buf = bv.buffer();
158         DocIterator cell_dit = undo.cell.asDocIterator(&buf.inset());
159
160         doRecordUndo(Undo::ATOMIC, cell_dit,
161                    undo.from, cell_dit.lastpit() - undo.end, bv.cursor(),
162                          undo.bparams, undo.isFullBuffer,
163                    otherstack);
164
165         // This does the actual undo/redo.
166         //lyxerr << "undo, performing: " << undo << std::endl;
167         bool labelsUpdateNeeded = false;
168         DocIterator dit = undo.cell.asDocIterator(&buf.inset());
169         if (undo.isFullBuffer) {
170                 BOOST_ASSERT(undo.pars);
171                 // This is a full document
172                 otherstack.top().bparams = buf.params();
173                 buf.params() = undo.bparams;
174                 std::swap(buf.paragraphs(), *undo.pars);
175                 delete undo.pars;
176                 undo.pars = 0;
177         } else if (dit.inMathed()) {
178                 // We stored the full cell here as there is not much to be
179                 // gained by storing just 'a few' paragraphs (most if not
180                 // all math inset cells have just one paragraph!)
181                 //lyxerr << "undo.array: " << *undo.array <<endl;
182                 BOOST_ASSERT(undo.array);
183                 dit.cell().swap(*undo.array);
184                 delete undo.array;
185                 undo.array = 0;
186         } else {
187                 // Some finer machinery is needed here.
188                 Text * text = dit.text();
189                 BOOST_ASSERT(text);
190                 BOOST_ASSERT(undo.pars);
191                 ParagraphList & plist = text->paragraphs();
192
193                 // remove new stuff between first and last
194                 ParagraphList::iterator first = plist.begin();
195                 advance(first, undo.from);
196                 ParagraphList::iterator last = plist.begin();
197                 advance(last, plist.size() - undo.end);
198                 plist.erase(first, last);
199
200                 // re-insert old stuff instead
201                 first = plist.begin();
202                 advance(first, undo.from);
203
204                 // this ugly stuff is needed until we get rid of the
205                 // inset_owner backpointer
206                 ParagraphList::iterator pit = undo.pars->begin();
207                 ParagraphList::iterator const end = undo.pars->end();
208                 for (; pit != end; ++pit)
209                         pit->setInsetOwner(dit.realInset());
210                 plist.insert(first, undo.pars->begin(), undo.pars->end());
211                 delete undo.pars;
212                 undo.pars = 0;
213                 labelsUpdateNeeded = true;
214         }
215         BOOST_ASSERT(undo.pars == 0);
216         BOOST_ASSERT(undo.array == 0);
217
218         // Set cursor
219         Cursor & cur = bv.cursor();
220         cur.setCursor(undo.cursor.asDocIterator(&buf.inset()));
221         cur.selection() = false;
222         cur.resetAnchor();
223         cur.fixIfBroken();
224         
225         if (labelsUpdateNeeded)
226                 updateLabels(buf);
227         finishUndo();
228         return true;
229 }
230
231 } // namespace anon
232
233
234 void finishUndo()
235 {
236         // Make sure the next operation will be stored.
237         undo_finished = true;
238 }
239
240
241 bool textUndo(BufferView & bv)
242 {
243         return textUndoOrRedo(bv, bv.buffer().undostack(),
244                               bv.buffer().redostack());
245 }
246
247
248 bool textRedo(BufferView & bv)
249 {
250         return textUndoOrRedo(bv, bv.buffer().redostack(),
251                               bv.buffer().undostack());
252 }
253
254
255 void recordUndo(Undo::undo_kind kind,
256         Cursor & cur, pit_type first, pit_type last)
257 {
258         Buffer & buf = cur.bv().buffer();
259         recordUndo(kind, cur, first, last, buf.undostack());
260         buf.redostack().clear();
261         //lyxerr << "undostack:\n";
262         //for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
263         //      lyxerr << "  " << i << ": " << buf.undostack()[i] << std::endl;
264 }
265
266
267 void recordUndo(Cursor & cur, Undo::undo_kind kind)
268 {
269         recordUndo(kind, cur, cur.pit(), cur.pit());
270 }
271
272
273 void recordUndoInset(Cursor & cur, Undo::undo_kind kind)
274 {
275         Cursor c = cur;
276         c.pop();
277         Buffer & buf = cur.bv().buffer();
278         doRecordUndo(kind, c, c.pit(), c.pit(), cur,
279                      buf.params(), false, buf.undostack());
280 }
281
282
283 void recordUndoSelection(Cursor & cur, Undo::undo_kind kind)
284 {
285         recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit());
286 }
287
288
289 void recordUndo(Cursor & cur, Undo::undo_kind kind, pit_type from)
290 {
291         recordUndo(kind, cur, cur.pit(), from);
292 }
293
294
295 void recordUndo(Cursor & cur, Undo::undo_kind kind,
296         pit_type from, pit_type to)
297 {
298         recordUndo(kind, cur, from, to);
299 }
300
301
302 void recordUndoFullDocument(BufferView * bv)
303 {
304         Buffer & buf = bv->buffer();
305         doRecordUndo(
306                 Undo::ATOMIC,
307                 doc_iterator_begin(buf.inset()),
308                 0, buf.paragraphs().size() - 1,
309                 bv->cursor(),
310                 buf.params(),
311                 true,
312                 buf.undostack()
313         );
314         undo_finished = false;
315 }
316
317
318 } // namespace lyx