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