]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
Get rid of the global use_babel variable.
[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 #include "undo_funcs.h"
13 #include "lyxtext.h"
14 #include "funcrequest.h"
15 #include "BufferView.h"
16 #include "buffer.h"
17 #include "insets/updatableinset.h"
18 #include "insets/insettext.h"
19 #include "debug.h"
20 #include "support/LAssert.h"
21 #include "iterators.h"
22
23 #include <vector>
24
25 using std::vector;
26 using boost::shared_ptr;
27
28
29 /// The flag used by FinishUndo().
30 bool undo_finished;
31 /// Whether actions are not added to the undo stacks.
32 bool undo_frozen;
33
34 namespace {
35
36 /// Utility to return the cursor.
37 LyXCursor const & undoCursor(BufferView * bv)
38 {
39         if (bv->theLockingInset())
40                 return bv->theLockingInset()->cursor(bv);
41         return bv->text->cursor;
42 }
43
44
45 /**
46  * Returns a pointer to the very first Paragraph depending of where
47  * we are so it will return the first paragraph of the buffer or the
48  * first paragraph of the textinset we're in.
49  */
50 ParagraphList * undoParagraphs(BufferView * bv, int inset_id)
51 {
52         Inset * inset = bv->buffer()->getInsetFromID(inset_id);
53         if (inset) {
54                 ParagraphList * result = inset->getParagraphs(0);
55                 if (result && !result->empty())
56                         return result;
57         }
58         return &bv->text->ownerParagraphs();
59 }
60
61
62 /**
63  * Finish the undo operation in the case there was no entry
64  * on the stack to perform.
65  */
66 void finishNoUndo(BufferView * bv)
67 {
68         freezeUndo();
69         bv->unlockInset(bv->theLockingInset());
70         finishUndo();
71         bv->text->postPaint(0);
72         unFreezeUndo();
73 }
74
75
76 // Returns false if no undo possible.
77 bool textHandleUndo(BufferView * bv, Undo & undo)
78 {
79         Buffer * b = bv->buffer();
80
81         ParIterator const before = b->getParFromID(undo.number_of_before_par);
82         ParIterator const behind = b->getParFromID(undo.number_of_behind_par);
83         ParIterator const null   = b->par_iterator_end();
84
85         int const before_id = (before == null) ? -1 : before->id();
86         int const behind_id = (behind == null) ? -1 : behind->id();
87         //int inset_id        = (first->inInset()) ? first->inInset()->id() : -1;
88
89         lyxerr << "\nbefore_id: " << before_id << "\n";
90         //lyxerr << "first_id:  " << first_id  << "\n";
91         //lyxerr << "last_id:   " << last_id   << "\n";
92         lyxerr << "behind_id: " << behind_id << "\n";
93
94 #if 0
95         // If there's no before take the beginning
96         // of the document for redoing.
97         if (before == null) {
98                 LyXText * t = bv->text;
99                 int num = undo.number_of_inset_id;
100                 if (undo.number_of_inset_id >= 0) {
101                         Inset * in = bv->buffer()->getInsetFromID(num);
102                         if (in) {
103                                 t = in->getLyXText(bv);
104                         } else {
105                                 num = -1;
106                         }
107                 }
108                 t->setCursorIntern(undoParagraphs(bv, num)->begin(), 0);
109         }
110
111         // Set the right(new) inset-owner of the paragraph if there is any.
112         if (!undo.pars.empty()) {
113                 Inset * in = 0;
114                 if (before != null)
115                         in = before->inInset();
116                 else if (undo.number_of_inset_id >= 0)
117                         in = bv->buffer()->getInsetFromID(undo.number_of_inset_id);
118                 for (size_t i = 0, n = undo.pars.size(); i < n; ++i)
119                         undo.pars[i]->setInsetOwner(in);
120         }
121
122         // Replace the paragraphs with the undo informations.
123         vector<Paragraph *> deletelist;
124
125         // Now add old paragraphs to be deleted.
126         if (before != behind || (behind == null && before == null)) {
127                 ParagraphList::iterator deletepar;
128                 if (before != null) {
129                         deletepar = *before;
130                         ++deletepar;
131                 } else {
132                         deletepar = undoParagraphs(bv, undo.number_of_inset_id)->begin();
133                 }
134                 // this surprisingly fills the undo! (Andre')
135                 size_t par = 0;
136                 //while (deletepar && deletepar != *behind)
137                 while (deletepar != *behind) {
138                         deletelist.push_back(&*deletepar);
139                         ++deletepar;
140
141                         // A memory optimization for edit:
142                         // Only layout information
143                         // is stored in the undo. So restore
144                         // the text informations.
145                         if (undo.kind == Undo::EDIT) {
146                                 undo.pars[par]->setContentsFromPar(*deletelist.back());
147                                 ++par;
148                         }
149                 }
150         }
151
152         // The order here is VERY IMPORTANT. We have to set the right
153         // next/prev pointer in the paragraphs so that a rebuild of
154         // the LyXText works!!!
155
156         // Thread the end of the undo onto the par in front if any.
157         if (!undo.pars.empty()) {
158 #warning FIXME
159                 //undo.pars.back()->next(&**behind);
160                 //if (behind != null)
161                 //(&**behind)->previous(undo.pars.back());
162         }
163
164         // Put the new stuff in the list if there is one.
165         Paragraph * undopar = undo.pars.empty() ? 0 : undo.pars.front();
166         if (!undo.pars.empty()) {
167 #warning FIXME
168                 //undo.pars.front()->previous(&**before);
169                 if (before != null) {
170 #warning FIXME
171                         //(&**before)->next(undopar);
172                 } else {
173                         int id = undoParagraphs(bv, undo.number_of_inset_id)->begin()->id();
174                         ParIterator op = bv->buffer()->getParFromID(id);
175                         if (op != null && op->inInset()) {
176 #warning FIXME reimplementaion needed here
177                                 //static_cast<InsetText*>(op->inInset())->paragraph(undopar);
178                         } else {
179 #warning FIXME reimplementation needed here
180                                 //bv->buffer()->paragraphs.set(undopar);
181                         }
182                 }
183         } else {
184                 // We enter here on DELETE undo operations where we
185                 // have to substitue the second paragraph with the
186                 // first if the removed one is the first.
187                 if (before == null && behind != null) {
188                         int id = undoParagraphs(bv, undo.number_of_inset_id)->begin()->id();
189                         ParIterator op = bv->buffer()->getParFromID(id);
190                         if (op != null && op->inInset()) {
191 #warning FIXME reimplementation needed here
192                                 //static_cast<InsetText*>(op->inInset())->paragraph(&**behind);
193                         } else {
194 #warning FIXME reimplementation needed here
195                                 //bv->buffer()->paragraphs.set(&**behind);
196                         }
197                         undopar = &**behind;
198                 }
199         }
200
201
202         // Set the cursor for redoing.
203         // If we have a par before the undopar.
204         if (before != null) {
205                 Inset * it = before->inInset();
206                 if (it)
207                         it->getLyXText(bv)->setCursorIntern(*before, 0);
208                 else
209                         bv->text->setCursorIntern(*before, 0);
210         }
211
212 // we are not ready for this we cannot set the cursor for a paragraph
213 // which is not already in a row of LyXText!!!
214 #if 0
215         else { // otherwise this is the first one and we start here
216                 Inset * it = undopar->inInset();
217                 if (it)
218                         it->getLyXText(bv)->setCursorIntern(bv, undopar, 0);
219                 else
220                         bv->text->setCursorIntern(bv, undopar, 0);
221         }
222 #endif
223
224         UpdatableInset * it = 0;
225         if (undopar)
226                 it = static_cast<UpdatableInset*>(undopar->inInset());
227
228         LyXText * text = it ? it->getLyXText(bv) : bv->text;
229
230         ParagraphList::iterator endpar = text->ownerParagraphs().end();
231
232         // Calculate the endpar for redoing the paragraphs.
233         if (behind != end) {
234                 endpar = *behind;
235                 ++endpar;
236         }
237
238         text->redoParagraphs(text->cursor, endpar);
239         ParIterator tmppar =
240                 bv->buffer()->getParFromID(undo.number_of_cursor_par);
241
242         if (tmppar != end) {
243                 LyXText * t;
244                 Inset * it = tmppar->inInset();
245                 if (it) {
246                         FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
247                         it->localDispatch(cmd);
248                         t = it->getLyXText(bv);
249                 } else {
250                         t = bv->text;
251                 }
252                 t->setCursorIntern(*tmppar, undo.cursor_pos);
253                 // Clear any selection and set the selection
254                 // cursor for an evt. new selection.
255                 t->clearSelection();
256                 t->selection.cursor = t->cursor;
257                 t->updateCounters();
258         }
259
260         if (it) {
261                 bv->fitCursor();
262                 bv->updateInset(it);
263                 bv->text->setCursorIntern(bv->text->cursor.par(),
264                                           bv->text->cursor.pos());
265         }
266
267         // And here it's safe enough to delete all removed paragraphs.
268         vector<Paragraph *>::iterator pit = deletelist.begin();
269         for(; pit != deletelist.end(); ++pit) {
270 #warning FIXME
271                 //(*pit)->previous(0);
272                 //(*pit)->next(0);
273                 delete (*pit);
274         }
275
276         // Otherwise the undo destructor would delete the paragraphs
277         undo.pars.resize(0);
278
279 #endif
280
281         finishUndo();
282         bv->text->postPaint(0);
283         return true;
284 }
285
286
287 bool createUndo(BufferView * bv, Undo::undo_kind kind,
288         ParagraphList::iterator itfirst, ParagraphList::iterator itlast,
289         shared_ptr<Undo> & u)
290 {
291         Buffer * b = bv->buffer();
292
293         ParIterator null    = b->par_iterator_end();
294         ParIterator prev    = null;
295         ParIterator before  = null;
296         ParIterator first   = null;
297         ParIterator last    = null;
298         ParIterator behind  = null;
299
300         int const first_id  = itfirst->id();
301         int const last_id   = itlast->id();
302
303         for (ParIterator it = b->par_iterator_begin(); it != null; ++it) {
304                 if ((*it)->id() == first_id) {
305                         first = it;
306                         before = prev;
307                 }
308                 if ((*it)->id() == last_id) {
309                         last = it;
310                         behind = last;
311                         ++behind;
312                 }
313                 prev = it;
314         }
315
316         if (last == null)
317                 last = first;
318
319         int const before_id = (before == null) ? -1 : before->id();
320         int const behind_id = (behind == null) ? -1 : behind->id();
321         int inset_id        = (first->inInset()) ? first->inInset()->id() : -1;
322
323         lyxerr << "\nbefore_id: " << before_id << "\n";
324         lyxerr << "first_id:  " << first_id  << "\n";
325         lyxerr << "last_id:   " << last_id   << "\n";
326         lyxerr << "behind_id: " << behind_id << "\n";
327         lyxerr << "inset_id:  " << inset_id  << "\n";
328
329         ParagraphList * plist = 0;
330         if (first != null)
331                 plist = &first.plist();
332         else if (behind != null)
333                 plist = &behind.plist();
334         else if (!plist) {
335                 lyxerr << "plist from buffer (should this happen?)\n";
336                 plist = &b->paragraphs;
337         }
338
339         // Undo::EDIT and Undo::FINISH are
340         // always finished. (no overlapping there)
341         // overlapping only with insert and delete inside one paragraph:
342         // Nobody wants all removed  character
343         // appear one by one when undoing.
344         // EDIT is special since only layout information, not the
345         // contents of a paragaph are stored.
346         if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) {
347                 // Check whether storing is needed.
348                 if (!b->undostack.empty() &&
349                     b->undostack.top()->kind == kind &&
350                     b->undostack.top()->number_of_before_par == before_id &&
351                     b->undostack.top()->number_of_behind_par == behind_id) {
352                         // No undo needed.
353                         return false;
354                 }
355         }
356
357         // Create a new Undo.
358         std::vector<Paragraph *> undo_pars;
359
360         for (ParagraphList::iterator it = *first; it != *last; ++it) 
361                 undo_pars.push_back(new Paragraph(*it, true));
362         undo_pars.push_back(new Paragraph(**last, true));
363
364         // A memory optimization: Just store the layout
365         // information when only edit.
366         if (kind == Undo::EDIT)
367                 for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
368                         undo_pars[i]->clearContents();
369
370         int cursor_par = undoCursor(bv).par()->id();
371         int cursor_pos = undoCursor(bv).pos();
372
373         u.reset(new Undo(kind, inset_id,
374                 before_id, behind_id, cursor_par, cursor_pos, undo_pars));
375
376         undo_finished = false;
377         return true;
378 }
379
380
381 // Returns false if no undo possible.
382 bool textUndoOrRedo(BufferView * bv,
383         limited_stack<boost::shared_ptr<Undo> > & stack,
384                     limited_stack<boost::shared_ptr<Undo> > & /*otherstack*/)
385 {
386         //Buffer * b = bv->buffer();
387
388         if (stack.empty()) {
389                 finishNoUndo(bv);
390                 return false;
391         }
392
393         shared_ptr<Undo> undo = stack.top();
394         stack.pop();
395         finishUndo();
396
397         if (!undo_frozen) {
398 /*
399                 ParIterator p = b->getParFromID(undo->number_of_before_par);
400                 bool ok = false;
401                 ParagraphList::iterator first;
402                 // default constructed?
403                 ParIterator const end = b->par_iterator_end();
404                 if (p != end) {
405                         first = p.par();
406                         if (first->next())
407                                 first = first->next();
408                 } else
409                         first = undoParagraphs(bv, undo->number_of_inset_id)->begin();
410                 if (first) {
411                         shared_ptr<Undo> u;
412                         ParIterator behind = b->getParFromID(undo->number_of_behind_par);
413                         if (createUndo(bv, undo->kind, first, behind.par(), u))
414                                 otherstack.push(u);
415                 }
416 */
417         }
418
419         // Now we can unlock the inset for saftey because the inset
420         // pointer could be changed during the undo-function. Anyway
421         // if needed we have to lock the right inset/position if this
422         // is requested.
423         freezeUndo();
424         bv->unlockInset(bv->theLockingInset());
425         bool const ret = textHandleUndo(bv, *undo.get());
426         unFreezeUndo();
427         return ret;
428 }
429
430 } // namespace anon
431
432
433 void finishUndo()
434 {
435         // Makes sure the next operation will be stored.
436         undo_finished = true;
437 }
438
439
440 void freezeUndo()
441 {
442         // This is dangerous and for internal use only.
443         undo_frozen = true;
444 }
445
446
447 void unFreezeUndo()
448 {
449         // This is dangerous and for internal use only.
450         undo_frozen = false;
451 }
452
453
454 bool textUndo(BufferView * bv)
455 {
456         return textUndoOrRedo(bv, bv->buffer()->undostack,
457                               bv->buffer()->redostack);
458 }
459
460
461 bool textRedo(BufferView * bv)
462 {
463         return textUndoOrRedo(bv, bv->buffer()->redostack,
464                               bv->buffer()->undostack);
465 }
466
467
468 void setUndo(BufferView * bv, Undo::undo_kind kind,
469              ParagraphList::iterator first)
470 {
471         setUndo(bv, kind, first, first);
472 }
473
474
475 void setUndo(BufferView * bv, Undo::undo_kind kind,
476              ParagraphList::iterator first, ParagraphList::iterator last)
477 {
478 #warning DISABLED
479         //return;
480         if (!undo_frozen) {
481                 shared_ptr<Undo> u;
482                 if (createUndo(bv, kind, first, last, u))
483                         bv->buffer()->undostack.push(u);
484                 bv->buffer()->redostack.clear();
485         }
486 }
487
488
489 void setCursorParUndo(BufferView * bv)
490 {
491         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
492 }