]> git.lyx.org Git - lyx.git/blob - src/undo.C
just the next_rit check fixed
[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 "debug.h"
21 #include "BufferView.h"
22 #include "iterators.h"
23 #include "lyxtext.h"
24 #include "paragraph.h"
25
26 using lyx::paroffset_type;
27
28
29 /// The flag used by finishUndo().
30 bool undo_finished;
31
32 /// Whether actions are not added to the undo stacks.
33 bool undo_frozen;
34
35 Undo::Undo(undo_kind kind_, int text_, int index_,
36            int first_par_, int end_par_, int cursor_par_, int cursor_pos_)
37         :
38                 kind(kind_),
39                 text(text_),
40                 index(index_),
41                 first_par(first_par_),
42                 end_par(end_par_),
43                 cursor_par(cursor_par_),
44                 cursor_pos(cursor_pos_)
45 {}
46
47
48 namespace {
49
50 std::ostream & operator<<(std::ostream & os, Undo const & undo)
51 {
52         return os << " text: " << undo.text
53                 << " index: " << undo.index
54                 << " first: " << undo.first_par
55                 << " from end: " << undo.end_par
56                 << " cursor: " << undo.cursor_par
57                 << "/" << undo.cursor_pos;
58 }
59
60
61 // translates LyXText pointer into offset count from document begin
62 ParIterator text2pit(LyXText * text, int & tcount)
63 {
64         tcount = 0;
65         Buffer * buf = text->bv()->buffer();
66         ParIterator pit = buf->par_iterator_begin();
67         ParIterator end = buf->par_iterator_end();
68
69         // it.text() returns 0 for outermost text.
70         if (text == text->bv()->text)
71                 return pit;
72
73         for ( ; pit != end; ++pit, ++tcount)
74                 if (pit.text() == text)
75                         return pit;
76         lyxerr << "undo: should not happen" << std::endl;
77         return end;
78 }
79
80
81 // translates offset from buffer begin to ParIterator
82 ParIterator num2pit(BufferView * bv, int num)
83 {
84         Buffer * buf = bv->buffer();
85         ParIterator pit = buf->par_iterator_begin();
86         ParIterator end = buf->par_iterator_end();
87
88         for ( ; num && pit != end; ++pit, --num)
89                 ;
90         
91         if (pit != end)
92                 return pit;
93
94         // don't crash early...
95         lyxerr << "undo: num2pit: num: " << num << std::endl;
96         BOOST_ASSERT(false);
97         return buf->par_iterator_begin();
98 }
99
100
101 // translates offset from buffer begin to LyXText
102 LyXText * pit2text(BufferView * bv, ParIterator const & pit)
103 {
104         LyXText * text = pit.text();
105         return text ? text : bv->text;
106 }
107
108
109 void recordUndo(Undo::undo_kind kind,
110         LyXText * text, paroffset_type first_par, paroffset_type last_par,
111         limited_stack<Undo> & stack)
112 {
113         Buffer * buf = text->bv()->buffer();
114
115         int const end_par = text->ownerParagraphs().size() - last_par;
116
117         // Undo::ATOMIC are always recorded (no overlapping there).
118         // overlapping only with insert and delete inside one paragraph:
119         // nobody wants all removed character appear one by one when undoing.
120         if (!undo_finished && kind != Undo::ATOMIC) {
121                 // Check whether storing is needed.
122                 if (!buf->undostack().empty()
123                     && buf->undostack().top().kind == kind
124                     && buf->undostack().top().first_par == first_par
125                     && buf->undostack().top().end_par == end_par) {
126                         // No additonal undo recording needed -
127                         // effectively, we combine undo recordings to one.
128                         return;
129                 }
130         }
131
132         // make and push the Undo entry
133         int textnum;
134         ParIterator pit = text2pit(text, textnum);
135         stack.push(Undo(kind, textnum, pit.index(),
136                 first_par, end_par, text->cursor.par(), text->cursor.pos()));
137         lyxerr << "undo record: " << stack.top() << std::endl;
138
139         // record the relevant paragraphs
140         ParagraphList & undo_pars = stack.top().pars;
141
142         ParagraphList & plist = text->ownerParagraphs();
143         ParagraphList::iterator first = plist.begin();
144         advance(first, first_par);
145         ParagraphList::iterator last = plist.begin();
146         advance(last, last_par);
147
148         for (ParagraphList::iterator it = first; it != last; ++it) {
149                 undo_pars.push_back(*it);
150                 undo_pars.back().id(it->id());
151         }
152         undo_pars.push_back(*last);
153         undo_pars.back().id(last->id());
154
155         // and make sure that next time, we should be combining if possible
156         undo_finished = false;
157 }
158
159
160 // returns false if no undo possible
161 bool performUndoOrRedo(BufferView * bv, Undo const & undo)
162 {
163         lyxerr << "undo, performing: " << undo << std::endl;
164         ParIterator pit = num2pit(bv, undo.text);
165         LyXText * text = pit2text(bv, pit);
166         ParagraphList & plist = text->ownerParagraphs();
167
168         // remove new stuff between first and last
169         {
170                 ParagraphList::iterator first = plist.begin();
171                 advance(first, undo.first_par);
172                 ParagraphList::iterator last = plist.begin();
173                 advance(last, plist.size() - undo.end_par);
174                 plist.erase(first, ++last);
175         }
176
177         // re-insert old stuff instead
178         if (plist.empty()) {
179                 plist.assign(undo.pars.begin(), undo.pars.end());
180         } else {
181                 ParagraphList::iterator first = plist.begin();
182                 advance(first, undo.first_par);
183                 plist.insert(first, undo.pars.begin(), undo.pars.end());
184         }
185
186         // set cursor
187         lyxerr <<   "undo, text: " << undo.text
188                 << " inset: " << pit.inset()
189                 << " index: " << undo.index
190                 << std::endl;
191         InsetOld * inset = pit.inset();
192         if (inset) {
193                 // magic needed to cope with inset locking
194                 inset->edit(bv, undo.index);
195         }
196
197         // set cursor again to force the position to be the right one
198         text->setCursorIntern(undo.cursor_par, undo.cursor_pos);
199
200         // clear any selection
201         text->clearSelection();
202         text->selection.cursor = text->cursor;
203         text->updateCounters();
204
205         // rebreak the entire document
206         bv->text->fullRebreak();
207
208         finishUndo();
209         return true;
210 }
211
212
213 // returns false if no undo possible
214 bool textUndoOrRedo(BufferView * bv,
215         limited_stack<Undo> & stack, limited_stack<Undo> & otherstack)
216 {
217         if (stack.empty()) {
218                 // nothing to do
219                 freezeUndo();
220                 bv->unlockInset(bv->theLockingInset());
221                 finishUndo();
222                 unFreezeUndo();
223                 return false;
224         }
225
226         Undo undo = stack.top();
227         stack.pop();
228         finishUndo();
229
230         if (!undo_frozen) {
231                 otherstack.push(undo);
232                 otherstack.top().pars.clear();
233                 ParIterator pit = num2pit(bv, undo.text);
234                 ParagraphList & plist = pit.plist();
235                 if (undo.first_par + undo.end_par <= int(plist.size())) {
236                         ParagraphList::iterator first = plist.begin();
237                         advance(first, undo.first_par);
238                         ParagraphList::iterator last = plist.begin();
239                         advance(last, plist.size() - undo.end_par + 1);
240                         otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last);
241                 }
242                 LyXText * text = pit2text(bv, pit);
243                 otherstack.top().cursor_pos = text->cursor.pos();
244                 otherstack.top().cursor_par = text->cursor.par();
245                 lyxerr << " undo other: " << otherstack.top() << std::endl;
246         }
247
248         // Now we can unlock the inset for safety because the inset
249         // pointer could be changed during the undo-function. Anyway
250         // if needed we have to lock the right inset/position if this
251         // is requested.
252         freezeUndo();
253         bv->unlockInset(bv->theLockingInset());
254         bool const ret = performUndoOrRedo(bv, undo);
255         unFreezeUndo();
256         return ret;
257 }
258
259 } // namespace anon
260
261
262 void freezeUndo()
263 {
264         // this is dangerous and for internal use only
265         undo_frozen = true;
266 }
267
268
269 void unFreezeUndo()
270 {
271         // this is dangerous and for internal use only
272         undo_frozen = false;
273 }
274
275
276 void finishUndo()
277 {
278         // makes sure the next operation will be stored
279         undo_finished = true;
280 }
281
282
283 bool textUndo(BufferView * bv)
284 {
285         return textUndoOrRedo(bv, bv->buffer()->undostack(),
286                               bv->buffer()->redostack());
287 }
288
289
290 bool textRedo(BufferView * bv)
291 {
292         return textUndoOrRedo(bv, bv->buffer()->redostack(),
293                               bv->buffer()->undostack());
294 }
295
296
297 void recordUndo(Undo::undo_kind kind,
298         LyXText const * text, paroffset_type first, paroffset_type last)
299 {
300         if (undo_frozen)
301                 return;
302         Buffer * buf = text->bv()->buffer();
303         recordUndo(kind, const_cast<LyXText *>(text), first, last, buf->undostack());
304         buf->redostack().clear();
305 }
306
307
308 void recordUndo(Undo::undo_kind kind, LyXText const * text, paroffset_type par)
309 {
310         recordUndo(kind, text, par, par);
311 }
312
313
314 void recordUndo(BufferView * bv, Undo::undo_kind kind)
315 {
316         recordUndo(kind, bv->text, bv->text->cursor.par());
317 }