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