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