]> git.lyx.org Git - lyx.git/blob - src/undo.C
b3e3334d37420cba3e79a337687062885afeefe0
[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 "lyxtext.h"
24 #include "paragraph.h"
25
26 #include "mathed/math_support.h"
27
28 #include <algorithm>
29
30 using lyx::paroffset_type;
31
32
33 namespace {
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 << " from: " << undo.from
42                 << " end: " << undo.end
43                 << " cursor:\n" << undo.cursor;
44 }
45
46
47 void recordUndo(Undo::undo_kind kind,
48         LCursor & cur, paroffset_type first_par, paroffset_type last_par,
49         limited_stack<Undo> & stack)
50 {
51         BOOST_ASSERT(first_par <= cur.lastpar());
52         BOOST_ASSERT(last_par <= cur.lastpar());
53
54         if (first_par > last_par)
55                 std::swap(first_par, last_par);
56
57         // create the position information of the Undo entry
58         Undo undo;
59         undo.kind = kind;
60         undo.cursor = StableDocumentIterator(cur);
61         undo.from = first_par;
62         undo.end = cur.lastpar() - last_par;
63
64         // Undo::ATOMIC are always recorded (no overlapping there).
65         // As nobody wants all removed character appear one by one when undoing,
66         // we want combine 'similar' non-ATOMIC undo recordings to one.
67         if (!undo_finished
68             && kind != Undo::ATOMIC
69             && !stack.empty()
70             && stack.top().cursor.size() == undo.cursor.size()
71                   && stack.top().kind == undo.kind
72                   && stack.top().from == undo.from
73                   && stack.top().end == undo.end)
74                 return;
75
76         // fill in the real data to be saved
77         if (cur.inMathed()) {
78                 // simply use the whole cell
79                 undo.array = asString(cur.cell());
80         } else {
81                 // some more effort needed here as 'the whole cell' of the
82                 // main LyXText _is_ the whole document.
83                 // record the relevant paragraphs
84                 LyXText * text = cur.text();
85                 BOOST_ASSERT(text);
86                 ParagraphList & plist = text->paragraphs();
87                 ParagraphList::iterator first = plist.begin();
88                 advance(first, first_par);
89                 ParagraphList::iterator last = plist.begin();
90                 advance(last, last_par + 1);
91                 undo.pars = ParagraphList(first, last);
92         }
93
94         // push the undo entry to undo stack 
95         //lyxerr << "undo record: " << stack.top() << std::endl;
96         stack.push(undo);
97
98         // next time we'll try again to combine entries if possible
99         undo_finished = false;
100 }
101
102
103 void performUndoOrRedo(BufferView & bv, Undo const & undo)
104 {
105         LCursor & cur = bv.cursor();
106         lyxerr << "undo, performing: " << undo << std::endl;
107         cur.setCursor(undo.cursor.asDocumentIterator(&bv.buffer()->inset()), false);
108
109         if (cur.inMathed()) {
110                 // We stored the full cell here as there is not much to be
111                 // gained by storing just 'a few' paragraphs (most if not
112                 // all math inset cells have just one paragraph!)
113                 asArray(undo.array, cur.cell());
114         } else {
115                 // Some finer machinery is needed here.
116                 LyXText * text = cur.text();
117                 BOOST_ASSERT(text);
118                 ParagraphList & plist = text->paragraphs();
119
120                 // remove new stuff between first and last
121                 ParagraphList::iterator first = plist.begin();
122                 advance(first, undo.from);
123                 ParagraphList::iterator last = plist.begin();
124                 advance(last, plist.size() - undo.end);
125                 plist.erase(first, last);
126
127                 // re-insert old stuff instead
128                 first = plist.begin();
129                 advance(first, undo.from);
130                 plist.insert(first, undo.pars.begin(), undo.pars.end());
131         }
132
133         cur.resetAnchor();
134         finishUndo();
135 }
136
137
138 // returns false if no undo possible
139 bool textUndoOrRedo(BufferView & bv,
140         limited_stack<Undo> & stack, limited_stack<Undo> & otherstack)
141 {
142         if (stack.empty()) {
143                 // nothing to do
144                 finishUndo();
145                 return false;
146         }
147
148         Undo undo = stack.top();
149         stack.pop();
150         finishUndo();
151
152         // this implements redo
153         otherstack.push(undo);
154         DocumentIterator dit =
155                 undo.cursor.asDocumentIterator(&bv.buffer()->inset());
156         if (dit.inMathed()) {
157                 // not much to be done
158         } else {
159                 otherstack.top().pars.clear();
160                 LyXText * text = dit.text();
161                 BOOST_ASSERT(text);
162                 ParagraphList & plist = text->paragraphs();
163                 if (undo.from + undo.end <= int(plist.size())) {
164                         ParagraphList::iterator first = plist.begin();
165                         advance(first, undo.from);
166                         ParagraphList::iterator last = plist.begin();
167                         advance(last, plist.size() - undo.end);
168                         otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last);
169                 }
170         }
171         otherstack.top().cursor = bv.cursor();
172         //lyxerr << " undo other: " << otherstack.top() << std::endl;
173
174         performUndoOrRedo(bv, undo);
175         return true;
176 }
177
178 } // namespace anon
179
180
181 void finishUndo()
182 {
183         // makes sure the next operation will be stored
184         undo_finished = true;
185 }
186
187
188 bool textUndo(BufferView & bv)
189 {
190         return textUndoOrRedo(bv, bv.buffer()->undostack(),
191                               bv.buffer()->redostack());
192 }
193
194
195 bool textRedo(BufferView & bv)
196 {
197         return textUndoOrRedo(bv, bv.buffer()->redostack(),
198                               bv.buffer()->undostack());
199 }
200
201
202 void recordUndo(Undo::undo_kind kind,
203         LCursor & cur, paroffset_type first, paroffset_type last)
204 {
205         Buffer * buf = cur.bv().buffer();
206         recordUndo(kind, cur, first, last, buf->undostack());
207         buf->redostack().clear();
208         lyxerr << "undostack:\n";
209         for (size_t i = 0, n = buf->undostack().size(); i != n && i < 6; ++i)
210                 lyxerr << "  " << i << ": " << buf->undostack()[i] << std::endl;
211 }
212
213
214 void recordUndo(LCursor & cur, Undo::undo_kind kind)
215 {
216         recordUndo(kind, cur, cur.par(), cur.par());
217 }
218
219
220 void recordUndoSelection(LCursor & cur, Undo::undo_kind kind)
221 {
222         recordUndo(kind, cur, cur.selBegin().par(), cur.selEnd().par());
223 }
224
225
226 void recordUndo(LCursor & cur, Undo::undo_kind kind, paroffset_type from)
227 {
228         recordUndo(kind, cur, cur.par(), from);
229 }
230
231
232 void recordUndo(LCursor & cur, Undo::undo_kind kind,
233         paroffset_type from, paroffset_type to)
234 {
235         recordUndo(kind, cur, from, to);
236 }
237
238
239 void recordUndoFullDocument(LCursor &)
240 {
241         //recordUndo(Undo::ATOMIC,
242         //      cur, 0, cur.bv().text()->paragraphs().size() - 1);
243 }