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