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