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