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