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