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