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