]> git.lyx.org Git - lyx.git/blob - src/undo.C
hopefully fix tex2lyx linking.
[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 = 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                 asArray(undo.array, dit.cell());
174         } else {
175                 // Some finer machinery is needed here.
176                 LyXText * text = dit.text();
177                 BOOST_ASSERT(text);
178                 ParagraphList & plist = text->paragraphs();
179
180                 // remove new stuff between first and last
181                 ParagraphList::iterator first = plist.begin();
182                 advance(first, undo.from);
183                 ParagraphList::iterator last = plist.begin();
184                 advance(last, plist.size() - undo.end);
185                 plist.erase(first, last);
186
187                 // re-insert old stuff instead
188                 first = plist.begin();
189                 advance(first, undo.from);
190
191                 // this ugly stuff is needed until we get rid of the
192                 // inset_owner backpointer
193                 ParagraphList::iterator pit = undo.pars.begin();
194                 ParagraphList::iterator const end = undo.pars.end();
195                 for (; pit != end; ++pit)
196                         pit->setInsetOwner(dit.realInset());
197                 plist.insert(first, undo.pars.begin(), undo.pars.end());
198         }
199
200         // Set cursor
201         LCursor & cur = bv.cursor();
202         cur.setCursor(undo.cursor.asDocIterator(&buf->inset()));
203         cur.selection() = false;
204         cur.resetAnchor();
205         finishUndo();
206
207         return true;
208 }
209
210 } // namespace anon
211
212
213 void finishUndo()
214 {
215         // Make sure the next operation will be stored.
216         undo_finished = true;
217 }
218
219
220 bool textUndo(BufferView & bv)
221 {
222         return textUndoOrRedo(bv, bv.buffer()->undostack(),
223                               bv.buffer()->redostack());
224 }
225
226
227 bool textRedo(BufferView & bv)
228 {
229         return textUndoOrRedo(bv, bv.buffer()->redostack(),
230                               bv.buffer()->undostack());
231 }
232
233
234 void recordUndo(Undo::undo_kind kind,
235         LCursor & cur, pit_type first, pit_type last)
236 {
237         Buffer * buf = cur.bv().buffer();
238         recordUndo(kind, cur, first, last, buf->undostack());
239         buf->redostack().clear();
240         //lyxerr << "undostack:\n";
241         //for (size_t i = 0, n = buf->undostack().size(); i != n && i < 6; ++i)
242         //      lyxerr << "  " << i << ": " << buf->undostack()[i] << std::endl;
243 }
244
245
246 void recordUndo(LCursor & cur, Undo::undo_kind kind)
247 {
248         recordUndo(kind, cur, cur.pit(), cur.pit());
249 }
250
251
252 void recordUndoInset(LCursor & cur, Undo::undo_kind kind)
253 {
254         LCursor c = cur;
255         c.pop();
256         Buffer * buf = cur.bv().buffer();
257         doRecordUndo(kind, c, c.pit(), c.pit(), cur,
258                      buf->params(), false, buf->undostack());
259 }
260
261
262 void recordUndoSelection(LCursor & cur, Undo::undo_kind kind)
263 {
264         recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit());
265 }
266
267
268 void recordUndo(LCursor & cur, Undo::undo_kind kind, pit_type from)
269 {
270         recordUndo(kind, cur, cur.pit(), from);
271 }
272
273
274 void recordUndo(LCursor & cur, Undo::undo_kind kind,
275         pit_type from, pit_type to)
276 {
277         recordUndo(kind, cur, from, to);
278 }
279
280
281 void recordUndoFullDocument(BufferView * bv)
282 {
283         Buffer * buf = bv->buffer();
284         doRecordUndo(
285                 Undo::ATOMIC,
286                 doc_iterator_begin(buf->inset()),
287                 0, buf->paragraphs().size() - 1,
288                 bv->cursor(),
289                 buf->params(),
290                 true,
291                 buf->undostack()
292         );
293         undo_finished = false;
294 }
295
296
297 } // namespace lyx