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