]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
don't rm emergency saves ever
[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 "BufferView.h"
15 #include "buffer.h"
16 #include "insets/updatableinset.h"
17 #include "insets/insettext.h"
18 #include "debug.h"
19 #include "support/LAssert.h"
20 #include "iterators.h"
21
22 #include <vector>
23
24 using std::vector;
25 using boost::shared_ptr;
26
27
28 /// The flag used by FinishUndo().
29 bool undo_finished;
30 /// Whether actions are not added to the undo stacks.
31 bool undo_frozen;
32
33 namespace {
34
35 /// Utility to return the cursor.
36 LyXCursor const & undoCursor(BufferView * bv)
37 {
38         if (bv->theLockingInset())
39                 return bv->theLockingInset()->cursor(bv);
40         return bv->text->cursor;
41 }
42
43
44 /**
45  * Returns a pointer to the very first Paragraph depending of where
46  * we are so it will return the first paragraph of the buffer or the
47  * first paragraph of the textinset we're in.
48  */
49 ParagraphList undoParagraphs(BufferView * bv, int inset_id)
50 {
51         Inset * inset = bv->buffer()->getInsetFromID(inset_id);
52         if (inset) {
53                 ParagraphList * result = inset->getParagraphs(0);
54                 if (result && !result->empty())
55                         return *result;
56         }
57         return bv->text->ownerParagraphs();
58 }
59
60
61 /**
62  * Finish the undo operation in the case there was no entry
63  * on the stack to perform.
64  */
65 void finishNoUndo(BufferView * bv)
66 {
67         freezeUndo();
68         bv->unlockInset(bv->theLockingInset());
69         finishUndo();
70         bv->text->postPaint(0);
71         unFreezeUndo();
72 }
73
74
75 // Returns false if no undo possible.
76 bool textHandleUndo(BufferView * bv, Undo & undo)
77 {
78         Buffer * b = bv->buffer();
79
80         Paragraph * const before = &*b->getParFromID(undo.number_of_before_par);
81         Paragraph * const behind = &*b->getParFromID(undo.number_of_behind_par);
82
83         // If there's no before take the beginning
84         // of the document for redoing.
85         if (!before) {
86                 LyXText * t = bv->text;
87                 int num = undo.number_of_inset_id;
88                 if (undo.number_of_inset_id >= 0) {
89                         Inset * in = bv->buffer()->getInsetFromID(num);
90                         if (in) {
91                                 t = in->getLyXText(bv);
92                         } else {
93                                 num = -1;
94                         }
95                 }
96                 t->setCursorIntern(undoParagraphs(bv, num).begin(), 0);
97         }
98
99         // Set the right(new) inset-owner of the paragraph if there is any. 
100         if (!undo.pars.empty()) {
101                 Inset * in = 0;
102                 if (before)
103                         in = before->inInset();
104                 else if (undo.number_of_inset_id >= 0)
105                         in = bv->buffer()->getInsetFromID(undo.number_of_inset_id);
106                 for (size_t i = 0, n = undo.pars.size(); i < n; ++i)
107                         undo.pars[i]->setInsetOwner(in);
108         }
109
110         // Replace the paragraphs with the undo informations.
111         vector<Paragraph *> deletelist;
112
113         // Now add old paragraphs to be deleted.
114         if (before != behind || (!behind && !before)) {
115                 Paragraph * deletepar;
116                 if (before)
117                         deletepar = before->next();
118                 else
119                         deletepar = &undoParagraphs(bv, undo.number_of_inset_id).front();
120                 // this surprisingly fills the undo! (Andre')
121                 size_t par = 0;
122                 while (deletepar && deletepar != behind) {
123                         deletelist.push_back(deletepar);
124                         deletepar = deletepar->next();
125
126                         // A memory optimization for edit:
127                         // Only layout information
128                         // is stored in the undo. So restore
129                         // the text informations.
130                         if (undo.kind == Undo::EDIT) {
131                                 undo.pars[par]->setContentsFromPar(*deletelist.back());
132                                 ++par;
133                         }
134                 }
135         }
136
137         // The order here is VERY IMPORTANT. We have to set the right
138         // next/prev pointer in the paragraphs so that a rebuild of
139         // the LyXText works!!!
140
141         // Thread the end of the undo onto the par in front if any.
142         if (!undo.pars.empty()) {
143                 undo.pars.back()->next(behind);
144                 if (behind)
145                         behind->previous(undo.pars.back());
146         }
147
148         // Put the new stuff in the list if there is one.
149         Paragraph * undopar = undo.pars.empty() ? 0 : undo.pars.front();
150         if (!undo.pars.empty()) {
151                 undo.pars.front()->previous(before);
152                 if (before)
153                         before->next(undopar);
154                 else {
155                         int id = undoParagraphs(bv, undo.number_of_inset_id).front().id();
156                         Paragraph * op = &*bv->buffer()->getParFromID(id);
157                         if (op && op->inInset()) {
158                                 static_cast<InsetText*>(op->inInset())->paragraph(undopar);
159                         } else {
160                                 bv->buffer()->paragraphs.set(undopar);
161                         }
162                 }
163         } else {
164                 // We enter here on DELETE undo operations where we
165                 // have to substitue the second paragraph with the
166                 // first if the removed one is the first.
167                 if (!before && behind) {
168                         int id = undoParagraphs(bv, undo.number_of_inset_id).front().id();
169                         Paragraph * op = &*bv->buffer()->getParFromID(id);
170                         if (op && op->inInset()) {
171                                 static_cast<InsetText*>(op->inInset())->paragraph(behind);
172                         } else {
173                                 bv->buffer()->paragraphs.set(behind);
174                         }
175                         undopar = behind;
176                 }
177         }
178
179
180         // Set the cursor for redoing.
181         // If we have a par before the undopar.
182         if (before) {
183                 Inset * it = before->inInset();
184                 if (it)
185                         it->getLyXText(bv)->setCursorIntern(before, 0);
186                 else
187                         bv->text->setCursorIntern(before, 0);
188         }
189
190 // we are not ready for this we cannot set the cursor for a paragraph
191 // which is not already in a row of LyXText!!!
192 #if 0
193         else { // otherwise this is the first one and we start here
194                 Inset * it = undopar->inInset();
195                 if (it)
196                         it->getLyXText(bv)->setCursorIntern(bv, undopar, 0);
197                 else
198                         bv->text->setCursorIntern(bv, undopar, 0);
199         }
200 #endif
201
202         Paragraph * endpar = 0;
203
204         // Calculate the endpar for redoing the paragraphs.
205         if (behind)
206                 endpar = behind->next();
207
208         UpdatableInset * it = 0;
209         if (undopar)
210                 it = static_cast<UpdatableInset*>(undopar->inInset());
211         if (it) {
212                 it->getLyXText(bv)->redoParagraphs(
213                                                    it->getLyXText(bv)->cursor,
214                                                    endpar);
215                 Paragraph * tmppar =
216                         &*bv->buffer()->getParFromID(undo.number_of_cursor_par);
217                 if (tmppar) {
218                         it = static_cast<UpdatableInset*>(tmppar->inInset());
219                         LyXText * t;
220                         if (it) {
221                                 it->edit(bv);
222                                 t = it->getLyXText(bv);
223                         } else {
224                                 t = bv->text;
225                         }
226                         t->setCursorIntern(tmppar, undo.cursor_pos);
227                         // Clear any selection and set the selection
228                         // cursor for an evt. new selection.
229                         t->clearSelection();
230                         t->selection.cursor = t->cursor;
231                         t->updateCounters();
232                         bv->fitCursor();
233                 }
234                 bv->updateInset(it);
235                 bv->text->setCursorIntern(bv->text->cursor.par(),
236                                           bv->text->cursor.pos());
237         } else {
238                 bv->text->redoParagraphs(bv->text->cursor, endpar);
239                 Paragraph * tmppar =
240                         &*bv->buffer()->getParFromID(undo.number_of_cursor_par);
241                 if (tmppar) {
242                         LyXText * t;
243                         Inset * it = tmppar->inInset();
244                         if (it) {
245                                 it->edit(bv);
246                                 t = it->getLyXText(bv);
247                         } else {
248                                 t = bv->text;
249                         }
250                         t->setCursorIntern(tmppar, undo.cursor_pos);
251                         // Clear any selection and set the selection
252                         // cursor for an evt. new selection.
253                         t->clearSelection();
254                         t->selection.cursor = t->cursor;
255                         t->updateCounters();
256                 }
257         }
258
259         // And here it's safe enough to delete all removed paragraphs.
260         vector<Paragraph *>::iterator pit = deletelist.begin();
261         for(; pit != deletelist.end(); ++pit) {
262                 (*pit)->previous(0);
263                 (*pit)->next(0);
264                 delete (*pit);
265         }
266
267         // Otherwise the undo destructor would delete the paragraphs
268         undo.pars.resize(0);
269
270         finishUndo();
271         bv->text->postPaint(0);
272         return true;
273 }
274
275
276 bool createUndo(BufferView * bv, Undo::undo_kind kind,
277         ParagraphList::iterator itfirst, ParagraphList::iterator itbehind,
278         shared_ptr<Undo> & u)
279 {
280         Paragraph * const first = &*itfirst;
281         Paragraph * const behind = &*itbehind;
282         lyx::Assert(first);
283
284         int before_number = -1;
285         int behind_number = -1;
286         int inset_id = -1;
287
288         if (first->previous())
289                 before_number = first->previous()->id();
290         if (behind)
291                 behind_number = behind->id();
292         if (first->inInset())
293                 inset_id = first->inInset()->id();
294
295         Buffer * b = bv->buffer();
296
297         // Undo::EDIT  and Undo::FINISH are
298         // always finished. (no overlapping there)
299         // overlapping only with insert and delete inside one paragraph:
300         // Nobody wants all removed  character
301         // appear one by one when undoing.
302         // EDIT is special since only layout information, not the
303         // contents of a paragaph are stored.
304         if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) {
305                 // Check whether storing is needed.
306                 if (!b->undostack.empty() &&
307                     b->undostack.top()->kind == kind &&
308                     b->undostack.top()->number_of_before_par == before_number &&
309                     b->undostack.top()->number_of_behind_par == behind_number) {
310                         // No undo needed.
311                         return false;
312                 }
313         }
314
315         // Create a new Undo.
316         std::vector<Paragraph *> undo_pars;
317
318         Paragraph const * end = 0;
319
320         if (behind)
321                 end = behind->previous();
322         else {
323                 end = first;
324                 while (end->next())
325                         end = end->next();
326         }
327
328         if (first && end && (first != end->next()) &&
329             ((before_number != behind_number) ||
330                  ((before_number < 0) && (behind_number < 0))))
331         {
332                 undo_pars.push_back(new Paragraph(*first, true));
333                 for (Paragraph * tmppar = first; tmppar != end && tmppar->next(); ) {
334                         tmppar = tmppar->next();
335                         undo_pars.push_back(new Paragraph(*tmppar, true));
336                         size_t const n = undo_pars.size();
337                         undo_pars[n - 2]->next(undo_pars[n - 1]);
338                         undo_pars[n - 1]->previous(undo_pars[n - 2]);
339                 }
340                 undo_pars.back()->next(0);
341         }
342
343         // A memory optimization: Just store the layout
344         // information when only edit.
345         if (kind == Undo::EDIT) {
346                 for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
347                         undo_pars[i]->clearContents();
348         }               
349
350         int cursor_par = undoCursor(bv).par()->id();
351         int cursor_pos = undoCursor(bv).pos();
352
353         //lyxerr << "createUndo: inset_id: " << inset_id << "  before_number: " 
354         //      << before_number << "  behind_number: " << behind_number << "\n";
355         u.reset(new Undo(kind, inset_id,
356                 before_number, behind_number,
357                 cursor_par, cursor_pos, undo_pars));
358
359         undo_finished = false;
360         return true;
361 }
362
363
364 // Returns false if no undo possible.
365 bool textUndoOrRedo(BufferView * bv,
366         limited_stack<boost::shared_ptr<Undo> > & stack,
367         limited_stack<boost::shared_ptr<Undo> > & otherstack)
368 {
369         Buffer * b = bv->buffer();
370
371         if (stack.empty()) {
372                 finishNoUndo(bv);
373                 return false;
374         }
375
376         shared_ptr<Undo> undo = stack.top();
377         stack.pop();
378         finishUndo();
379
380         if (!undo_frozen) {
381                 Paragraph * first = &*b->getParFromID(undo->number_of_before_par);
382                 if (first && first->next())
383                         first = first->next();
384                 else if (!first)
385                         first = &*undoParagraphs(bv, undo->number_of_inset_id).begin();
386                 if (first) {
387                         shared_ptr<Undo> u;
388                         if (createUndo(bv, undo->kind, first,
389                                              b->getParFromID(undo->number_of_behind_par), u))
390                                 otherstack.push(u);
391                 }
392         }
393
394         // Now we can unlock the inset for saftey because the inset
395         // pointer could be changed during the undo-function. Anyway
396         // if needed we have to lock the right inset/position if this
397         // is requested.
398         freezeUndo();
399         bv->unlockInset(bv->theLockingInset());
400         bool const ret = textHandleUndo(bv, *undo.get());
401         unFreezeUndo();
402         return ret;
403 }
404
405 } // namespace anon
406
407
408 void finishUndo()
409 {
410         // Makes sure the next operation will be stored.
411         undo_finished = true;
412 }
413
414
415 void freezeUndo()
416 {
417         // This is dangerous and for internal use only.
418         undo_frozen = true;
419 }
420
421
422 void unFreezeUndo()
423 {
424         // This is dangerous and for internal use only.
425         undo_frozen = false;
426 }
427
428
429 bool textUndo(BufferView * bv)
430 {
431         return textUndoOrRedo(bv, bv->buffer()->undostack,
432                               bv->buffer()->redostack);
433 }
434
435
436 bool textRedo(BufferView * bv)
437 {
438         return textUndoOrRedo(bv, bv->buffer()->redostack,
439                               bv->buffer()->undostack);
440 }
441
442
443 void setUndo(BufferView * bv, Undo::undo_kind kind,
444              ParagraphList::iterator first, ParagraphList::iterator behind)
445 {
446         if (!undo_frozen) {
447                 shared_ptr<Undo> u;
448                 if (createUndo(bv, kind, first, behind, u))
449                         bv->buffer()->undostack.push(u);
450                 bv->buffer()->redostack.clear();
451         }
452 }
453
454
455 void setRedo(BufferView * bv, Undo::undo_kind kind,
456              ParagraphList::iterator first, ParagraphList::iterator behind)
457 {
458         shared_ptr<Undo> u;
459         if (createUndo(bv, kind, first, behind, u))
460                 bv->buffer()->redostack.push(u);
461 }
462
463
464 void setCursorParUndo(BufferView * bv)
465 {
466         setUndo(bv, Undo::FINISH, bv->text->cursor.par(),
467                 boost::next(bv->text->cursor.par()));
468 }