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