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