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