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