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