]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
Fixed undo crash in first paragraph by avoiding special case and doing a
[lyx.git] / src / undo_funcs.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995-2001 The LyX Team.
7  *
8  * ====================================================== */
9
10 #include <config.h>
11
12 #include "undo_funcs.h"
13 #include "lyxtext.h"
14 #include "funcrequest.h"
15 #include "BufferView.h"
16 #include "buffer.h"
17 #include "insets/updatableinset.h"
18 #include "insets/insettext.h"
19 #include "debug.h"
20 #include "support/LAssert.h"
21 #include "iterators.h"
22
23
24 /// The flag used by FinishUndo().
25 bool undo_finished;
26 /// Whether actions are not added to the undo stacks.
27 bool undo_frozen;
28
29 namespace {
30
31
32 /**
33  * Finish the undo operation in the case there was no entry
34  * on the stack to perform.
35  */
36 void finishNoUndo(BufferView * bv)
37 {
38         freezeUndo();
39         bv->unlockInset(bv->theLockingInset());
40         finishUndo();
41         bv->text->postPaint();
42         unFreezeUndo();
43 }
44
45
46 // Returns false if no undo possible.
47 bool textHandleUndo(BufferView * bv, Undo & undo)
48 {
49         Buffer * buf = bv->buffer();
50
51         ParagraphList * plist = &buf->paragraphs;
52 /*
53         ParIterator null = buf->par_iterator_end();
54
55         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it)
56                 if (it.plist().id() == undo.plist_id) {
57                         plist = &it.plist();
58                         break;
59                 }
60 */
61
62         // Set the right(new) inset-owner of the paragraph if there is any.
63         UpdatableInset * inset =
64                 static_cast<UpdatableInset *>(buf->getInsetFromID(undo.inset_id));
65
66         ParagraphList::iterator pit = undo.pars.begin();
67         ParagraphList::iterator end = undo.pars.end();
68         for ( ; pit != end; ++pit)
69                 pit->setInsetOwner(inset);
70
71         //lyxerr << "\nhandle: inset_id: " << undo.inset_id << "\n";
72         //lyxerr << "handle: inset: " << inset << "\n";
73         //lyxerr << "handle: plist_id: " << undo.plist_id << "\n";
74         lyxerr << "handle: undo.pars.size(): " << undo.pars.size() << "\n";
75         lyxerr << "handle: first_offset:  " << undo.first_par_offset  << "\n";
76         lyxerr << "handle: last_offset: " << undo.last_par_offset << "\n";
77
78         // remove stuff between first and behind
79         {
80                 ParagraphList::iterator first = plist->begin();
81                 advance(first, undo.first_par_offset);
82                 ParagraphList::iterator last = plist->begin();
83                 advance(last, plist->size() - undo.last_par_offset);
84                 lyxerr << "handle: first_id:  " << first->id()  << "\n";
85                 lyxerr << "handle: last_id: " << last->id() << "\n";
86                 lyxerr << "handle: remove: " << distance(first, last) + 1 << " pars\n";
87                 plist->erase(first, ++last);
88                 lyxerr << "handle: after remove\n";
89         }
90
91         // re-insert old stuff
92         {
93                 ParagraphList::iterator first = plist->begin();
94                 advance(first, undo.first_par_offset);
95                 lyxerr << "handle: plist->size: " << plist->size() << "\n";
96                 lyxerr << "handle: offset: " << undo.first_par_offset << "\n";
97                 plist->insert(first, undo.pars.begin(), undo.pars.end());
98                 lyxerr << "handle: after insert\n";
99         }
100
101         /*
102                 // A memory optimization for edit:
103                 // Only layout information
104                 // is stored in the undo. So restore
105                 // the text informations.
106                 if (undo.kind == Undo::EDIT) {
107                         undo.pars[par]->setContentsFromPar(*deletelist.back());
108                         ++par;
109                 }
110         */
111
112         // redo Paragraphs  (should be handled outside undo...)
113         {
114                 //LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
115                 LyXText * text = bv->text;
116                 lyxerr << "handle: text: " << text << "\n";
117                 // The cursor should be sane, so we will put it to the top
118                 text->setCursorIntern(plist->begin(), 0);
119                 lyxerr << "handle: 3\n";
120                 // Rebreak the entire document
121                 text->fullRebreak();
122                 lyxerr << "handle: after redo\n";
123
124                 if (inset) {
125                         FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
126                         inset->localDispatch(cmd);
127                 }
128         }
129
130         if (inset) {
131                 lyxerr << "fit cursor...\n";
132                 bv->fitCursor();
133                 bv->updateInset(inset);
134         }
135
136         // set cursor
137         {
138                 LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
139                 ParagraphList::iterator cursor = text->ownerParagraphs().begin();
140                 advance(cursor, undo.cursor_par_offset);
141                 text->setCursorIntern(cursor, undo.cursor_pos);
142                 // Clear any selection and set the selection
143                 // cursor for an evt. new selection.
144                 text->clearSelection();
145                 text->selection.cursor = text->cursor;
146                 text->updateCounters();
147                 lyxerr << "after setCursor\n";
148         }
149
150
151         finishUndo();
152         bv->text->postPaint();
153
154         lyxerr << "finished  textHandleUndo...\n";
155         return true;
156 }
157
158
159 void createUndo(BufferView * bv, Undo::undo_kind kind,
160         ParagraphList::iterator first, ParagraphList::iterator last,
161         limited_stack<Undo> & stack)
162 {
163         Buffer * buf = bv->buffer();
164
165         ParagraphList * plist = 0;
166         ParIterator null = buf->par_iterator_end();
167
168         lyxerr << "\n";
169
170         UpdatableInset * inset = first->inInset();
171         LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
172         int const inset_id = inset ? inset->id() : -1;
173
174 #if 0
175         // this is what we'd like to have in the end for small grained undo
176         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it) {
177                 if (it->id() == first->id()) {
178                         plist = &it.plist();
179                         break;
180                 }
181         }
182
183 #else
184
185         // and that's the big stick we wield now
186         lyxerr << "create: first_id orig:   " << first->id()  << "\n";
187         lyxerr << "create: last_id orig:    " << last->id()   << "\n";
188         plist = &buf->paragraphs;
189         // this is what we'd like to have in the end for small grained undo
190         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it) {
191                 // This does not work if we are nested twice or more
192                 if (it->id() == first->id())
193                         first = it.outerPar();
194                 if (it->id() == last->id())
195                         last = it.outerPar();
196         }
197
198 #endif
199
200         int const first_offset = std::distance(plist->begin(), first);
201         int const last_offset  = std::distance(last, plist->end());
202
203         if (last == plist->end()) {
204                 lyxerr << "*** createUndo: last == end  should not happen\n";
205         }
206
207
208         // Undo::EDIT and Undo::FINISH are always finished.
209         // (no overlapping there)
210         // overlapping only with insert and delete inside one paragraph:
211         // Nobody wants all removed character appear one by one when undoing.
212         // EDIT is special since only layout information, not the
213         // contents of a paragaph are stored.
214         if (! undo_finished && kind != Undo::EDIT && kind != Undo::FINISH) {
215                 // Check whether storing is needed.
216                 if (! buf->undostack.empty() 
217                     && buf->undostack.top().kind == kind 
218                     && buf->undostack.top().first_par_offset == first_offset
219                     && buf->undostack.top().last_par_offset == last_offset) {
220                         // No undo recording needed.
221                         return;
222                 }
223         }
224
225         int const cursor_offset = std::distance
226                 (text->ownerParagraphs().begin(), text->cursor.par());
227
228         //lyxerr << "create: plist_id:      " << plist->id()  << "\n";
229         //lyxerr << "create: inset_id:      " << inset_id  << "\n";
230         lyxerr << "create: first_id:      " << first->id()  << "\n";
231         lyxerr << "create: last_id:       " << last->id()   << "\n";
232         lyxerr << "create: first_offset:  " << first_offset  << "\n";
233         lyxerr << "create: last_offset:   " << last_offset   << "\n";
234         lyxerr << "create: cursor_offset: " << cursor_offset   << "\n";
235         lyxerr << "create: cursor_pos:    " << text->cursor.pos() << "\n";
236
237         stack.push(Undo(kind, inset_id, 0, //plist->id(),
238                 first_offset, last_offset,
239                 cursor_offset, text->cursor.pos(),
240                 ParagraphList()));
241
242         ParagraphList & undo_pars = stack.top().pars;
243
244         for (ParagraphList::iterator it = first; it != last; ++it) {
245                 undo_pars.push_back(*it);
246                 undo_pars.back().id(it->id());
247         }
248         undo_pars.push_back(*last);
249         undo_pars.back().id(last->id());
250
251         // A memory optimization: Just store the layout
252         // information when only edit.
253 #warning Waste...
254         //if (kind == Undo::EDIT)
255         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
256         //              undo_pars[i]->clearContents();
257
258         undo_finished = false;
259 }
260
261
262 // Returns false if no undo possible.
263 bool textUndoOrRedo(BufferView * bv,
264         limited_stack<Undo> & stack,
265         limited_stack<Undo> & otherstack)
266 {
267         if (stack.empty()) {
268                 finishNoUndo(bv);
269                 return false;
270         }
271
272         Undo undo = stack.top();
273         stack.pop();
274         finishUndo();
275
276         if (!undo_frozen) {
277                 otherstack.push(undo);
278                 otherstack.top().pars.clear();
279                 Buffer * buf = bv->buffer();
280                 ParagraphList & plist = buf->paragraphs;
281                 lyxerr << "\nredo: first: " << undo.first_par_offset << "\n";
282                 lyxerr << "redo: last:  " << undo.last_par_offset << "\n";
283                 lyxerr << "redo: size:  " << plist.size() << "\n";
284                 if (undo.first_par_offset + undo.last_par_offset <= int(plist.size())) {
285                         ParagraphList::iterator first = plist.begin();
286                         advance(first, undo.first_par_offset);
287                         ParagraphList::iterator last = plist.begin();
288                         advance(last, plist.size() - undo.last_par_offset + 1);
289                         otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last);
290                 }
291         }
292
293         // Now we can unlock the inset for safety because the inset
294         // pointer could be changed during the undo-function. Anyway
295         // if needed we have to lock the right inset/position if this
296         // is requested.
297         freezeUndo();
298         bv->unlockInset(bv->theLockingInset());
299         bool const ret = textHandleUndo(bv, undo);
300         unFreezeUndo();
301         return ret;
302 }
303
304 } // namespace anon
305
306
307 void finishUndo()
308 {
309         // Makes sure the next operation will be stored.
310         undo_finished = true;
311 }
312
313
314 void freezeUndo()
315 {
316         // This is dangerous and for internal use only.
317         undo_frozen = true;
318 }
319
320
321 void unFreezeUndo()
322 {
323         // This is dangerous and for internal use only.
324         undo_frozen = false;
325 }
326
327
328 bool textUndo(BufferView * bv)
329 {
330         return textUndoOrRedo(bv, bv->buffer()->undostack,
331                               bv->buffer()->redostack);
332 }
333
334
335 bool textRedo(BufferView * bv)
336 {
337         return textUndoOrRedo(bv, bv->buffer()->redostack,
338                               bv->buffer()->undostack);
339 }
340
341
342 void setUndo(BufferView * bv, Undo::undo_kind kind,
343              ParagraphList::iterator first, ParagraphList::iterator last)
344 {
345         if (!undo_frozen) {
346                 createUndo(bv, kind, first, last, bv->buffer()->undostack);
347                 bv->buffer()->redostack.clear();
348         }
349 }
350
351
352 void setUndo(BufferView * bv, Undo::undo_kind kind,
353              ParagraphList::iterator first)
354 {
355         setUndo(bv, kind, first, first);
356 }
357
358
359 void setUndo(BufferView * bv, Undo::undo_kind kind)
360 {
361         setUndo(bv, kind, bv->text->cursor.par());
362 }
363
364
365 void setCursorParUndo(BufferView * bv)
366 {
367         setUndo(bv, Undo::FINISH);
368 }