]> git.lyx.org Git - lyx.git/blob - src/undo.C
more cursor dispatch
[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         buf.text().fullRebreak();
190
191         ParIterator pit2 = num2pit(buf, undo.text);
192         advance(pit2, undo.cursor_par);
193         bv.setCursor(pit2, 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                 otherstack.top().cursor_pos = bv.cursor().pos();
228                 otherstack.top().cursor_par = bv.cursor().par();
229                 lyxerr << " undo other: " << otherstack.top() << std::endl;
230         }
231
232         freezeUndo();
233         bool const ret = performUndoOrRedo(bv, undo);
234         unFreezeUndo();
235         return ret;
236 }
237
238 } // namespace anon
239
240
241 void freezeUndo()
242 {
243         // this is dangerous and for internal use only
244         undo_frozen = true;
245 }
246
247
248 void unFreezeUndo()
249 {
250         // this is dangerous and for internal use only
251         undo_frozen = false;
252 }
253
254
255 void finishUndo()
256 {
257         // makes sure the next operation will be stored
258         undo_finished = true;
259 }
260
261
262 bool textUndo(BufferView & bv)
263 {
264         return textUndoOrRedo(bv, bv.buffer()->undostack(),
265                               bv.buffer()->redostack());
266 }
267
268
269 bool textRedo(BufferView & bv)
270 {
271         return textUndoOrRedo(bv, bv.buffer()->redostack(),
272                               bv.buffer()->undostack());
273 }
274
275
276 void recordUndo(Undo::undo_kind kind,
277         LCursor & cur, paroffset_type first, paroffset_type last)
278 {
279         if (undo_frozen)
280                 return;
281         Buffer * buf = cur.bv().buffer();
282         recordUndo(kind, cur, first, last, buf->undostack());
283         buf->redostack().clear();
284 }
285
286
287 void recordUndo(LCursor & cur, Undo::undo_kind kind)
288 {
289         recordUndo(kind, cur, cur.par(), cur.par());
290 }
291
292
293 void recordUndo(LCursor & cur, Undo::undo_kind kind, paroffset_type from)
294 {
295         recordUndo(kind, cur, cur.par(), from);
296 }
297
298
299 void recordUndo(LCursor & cur, Undo::undo_kind kind,
300         paroffset_type from, paroffset_type to)
301 {
302         recordUndo(kind, cur, from, to);
303 }
304
305
306 void recordUndoFullDocument(LCursor &)
307 {
308         //recordUndo(Undo::ATOMIC,
309         //      cur, 0, cur.bv().text()->paragraphs().size() - 1);
310 }