]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
fix
[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)
192                                 bv->text->ownerParagraph(behind);
193                 }
194                 if (tmppar4) {
195                         tmppar4->next(behind);
196                         if (behind)
197                                 behind->previous(tmppar4);
198                 }
199     
200     
201                 // Set the cursor for redoing
202                 if (before) {
203                         Inset * it = before->inInset();
204                         if (it)
205                                 it->getLyXText(bv)->setCursorIntern(bv, before, 0);
206                         else
207                                 bv->text->setCursorIntern(bv, before, 0);
208                 }
209
210                 Paragraph * endpar = 0;
211                 // calculate the endpar for redoing the paragraphs.
212                 if (behind)
213                         endpar = behind->next();
214     
215                 tmppar = bv->buffer()->getParFromID(undo->number_of_cursor_par);
216                 UpdatableInset* it = static_cast<UpdatableInset*>(tmppar3->inInset());
217                 if (it) {
218                         it->getLyXText(bv)->redoParagraphs(bv, it->getLyXText(bv)->cursor,
219                                                            endpar);
220                         LyXFont font;
221                         it->update(bv, font, false);
222                         // we now would have to rebreak the whole paragraph the undo-par
223                         // was in. How we do it here is not really true. We would have to
224                         // save this information in the undo-struct and then we could do
225                         // the right rebreak. Here we only handle the case where this
226                         // was in the actual paragraph, which not always is true.
227                         bv->text->redoParagraphs(bv, bv->text->cursor,
228                                                                          bv->text->cursor.par());
229                         if (tmppar){
230                                 it = static_cast<UpdatableInset*>(tmppar->inInset());
231                                 LyXText * t;
232                                 if (it) {
233                                         it->edit(bv);
234                                         t = it->getLyXText(bv);
235                                 } else {
236                                         t = bv->text;
237                                 }
238                                 t->setCursorIntern(bv, tmppar, undo->cursor_pos);
239                                 t->updateCounters(bv, t->cursor.row());
240                         }
241                         bv->text->setCursorIntern(bv, bv->text->cursor.par(),
242                                                                           bv->text->cursor.pos());
243                 } else {
244                         bv->text->redoParagraphs(bv, bv->text->cursor, endpar);
245                         if (tmppar) {
246                                 LyXText * t;
247                                 Inset * it = tmppar->inInset();
248                                 if (it) {
249                                         it->edit(bv);
250                                         t = it->getLyXText(bv);
251                                 } else {
252                                         t = bv->text;
253                                 }
254                                 t->setCursorIntern(bv, tmppar, undo->cursor_pos);
255                                 t->updateCounters(bv, t->cursor.row());
256                         }
257                 }
258                 result = true;
259                 delete undo;
260 #ifdef DELETE_UNUSED_PARAGRAPHS
261                 // And here it's save enough to delete all removed paragraphs
262                 std::vector<Paragraph *>::iterator pit = vvpar.begin();
263                 if (pit != vvpar.end()) {
264                         lyxerr << "DEL: ";
265                         for(;pit != vvpar.end(); ++pit) {
266                                 lyxerr << *pit << " ";
267                                 delete (*pit);
268                         }
269                         lyxerr << endl << "PARS:";
270                         ParIterator end = bv->buffer()->par_iterator_end();
271                         ParIterator it = bv->buffer()->par_iterator_begin();
272                         for (; it != end; ++it)
273                                 lyxerr << *it << " ";
274                         lyxerr << endl;
275                 }
276 #endif
277         }
278         finishUndo();
279         bv->text->status(bv, LyXText::NEED_MORE_REFRESH);
280         return result;
281 }
282
283
284 void finishUndo()
285 {
286         // makes sure the next operation will be stored
287         undo_finished = true;
288 }
289
290
291 void freezeUndo()
292 {
293         // this is dangerous and for internal use only
294         undo_frozen = true;
295 }
296
297
298 void unFreezeUndo()
299 {
300         // this is dangerous and for internal use only
301         undo_frozen = false;
302 }
303
304
305 void setUndo(BufferView * bv, Undo::undo_kind kind,
306              Paragraph const * first, Paragraph const * behind)
307 {
308         if (!undo_frozen) {
309                 bv->buffer()->undostack.push(createUndo(bv, kind, first, behind));
310                 bv->buffer()->redostack.clear();
311         }
312 }
313
314
315 void setRedo(BufferView * bv, Undo::undo_kind kind,
316              Paragraph const * first, Paragraph const * behind)
317 {
318         bv->buffer()->redostack.push(createUndo(bv, kind, first, behind));
319 }
320
321
322 Undo * createUndo(BufferView * bv, Undo::undo_kind kind,
323                   Paragraph const * first, Paragraph const * behind)
324 {
325         lyx::Assert(first);
326
327         int before_number = -1;
328         int behind_number = -1;
329         int inset_id = -1;
330
331         if (first->previous())
332                 before_number = first->previous()->id();
333         if (behind)
334                 behind_number = behind->id();
335         if (first->inInset())
336                 inset_id = first->inInset()->id();
337
338         // Undo::EDIT  and Undo::FINISH are
339         // always finished. (no overlapping there)
340         // overlapping only with insert and delete inside one paragraph: 
341         // Nobody wants all removed  character
342         // appear one by one when undoing. 
343         // EDIT is special since only layout information, not the
344         // contents of a paragaph are stored.
345         if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)){
346                 // check wether storing is needed
347                 if (!bv->buffer()->undostack.empty() && 
348                     bv->buffer()->undostack.top()->kind == kind &&
349                     bv->buffer()->undostack.top()->number_of_before_par == before_number &&
350                     bv->buffer()->undostack.top()->number_of_behind_par == behind_number) {
351                         // no undo needed
352                         return 0;
353                 }
354         }
355         // create a new Undo
356         Paragraph * undopar;
357
358         Paragraph * start = const_cast<Paragraph *>(first);
359         Paragraph * end = 0;
360
361         if (behind)
362                 end = const_cast<Paragraph*>(behind->previous());
363         else {
364                 end = start;
365                 while (end->next())
366                         end = end->next();
367         }
368         if (start && end && (start != end->next()) &&
369             ((before_number != behind_number) ||
370                  ((before_number < 0) && (behind_number < 0))))
371         {
372                 Paragraph * tmppar = start;
373                 Paragraph * tmppar2 = new Paragraph(*tmppar, true);
374                 
375                 // a memory optimization: Just store the layout information
376                 // when only edit
377                 if (kind == Undo::EDIT) {
378                         tmppar2->clearContents();
379                 }
380
381                 undopar = tmppar2;
382   
383                 while (tmppar != end && tmppar->next()) {
384                         tmppar = tmppar->next();
385                         tmppar2->next(new Paragraph(*tmppar, true));
386                         // a memory optimization: Just store the layout
387                         // information when only edit
388                         if (kind == Undo::EDIT) {
389                                 tmppar2->clearContents();
390                         }
391                         tmppar2->next()->previous(tmppar2);
392                         tmppar2 = tmppar2->next();
393                 }
394                 tmppar2->next(0);
395         } else
396                 undopar = 0; // nothing to replace (undo of delete maybe)
397
398         int cursor_par = undoCursor(bv).par()->id();
399         int cursor_pos =  undoCursor(bv).pos();
400         
401         Undo * undo = new Undo(kind, inset_id,
402                                before_number, behind_number,  
403                                cursor_par, cursor_pos, undopar);
404   
405         undo_finished = false;
406         return undo;
407 }
408
409
410 void setCursorParUndo(BufferView * bv)
411 {
412         setUndo(bv, Undo::FINISH, bv->text->cursor.par(),
413                 bv->text->cursor.par()->next());
414 }
415
416
417 Paragraph * firstUndoParagraph(BufferView * bv, int inset_id)
418 {
419         Inset * inset = bv->buffer()->getInsetFromID(inset_id);
420         if (inset) {
421                 Paragraph * result = inset->getFirstParagraph(0);
422                 if (result)
423                         return result;
424         }
425         return bv->text->ownerParagraph();
426 }
427
428
429 LyXCursor const & undoCursor(BufferView * bv)
430 {
431         if (bv->theLockingInset())
432                 return bv->theLockingInset()->cursor(bv);
433         return bv->text->cursor;
434 }