]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
parlist-19-a.diff
[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 const inset_id  = undo.number_of_inset_id;
88
89         ParagraphList * plist = undoParagraphs(bv, inset_id);
90
91         ParagraphList::iterator first;
92         if (before == null) {
93                 // if there's no before take the beginning of parlist.
94                 first = plist->begin();
95                 LyXText * t = bv->text;
96                 if (inset_id >= 0)
97                         if (Inset * in = bv->buffer()->getInsetFromID(inset_id))
98                                 t = in->getLyXText(bv);
99                 t->setCursorIntern(plist->begin(), 0);
100         } else {
101                 first = *before;
102                 ++first;
103         }
104         int const first_id  = first->id();
105
106         int after_id;
107         ParagraphList::iterator after;
108         if (behind == null) {
109                 // if there's no behind take the end of parlist.
110                 after = plist->end();
111                 after_id = -1;
112         } else {
113                 after = *behind;
114                 after_id = after->id();
115         }
116
117         lyxerr << "\nbefore_id: " << before_id << "\n";
118         lyxerr << "first_id:  " << first_id  << "\n";
119         lyxerr << "after_id: " << after_id << "\n";
120         lyxerr << "behind_id: " << behind_id << "\n";
121         lyxerr << "inset_id: " << inset_id << "\n";
122
123         // Set the right(new) inset-owner of the paragraph if there is any.
124         if (!undo.pars.empty()) {
125                 Inset * in = 0;
126                 if (before != null)
127                         in = before->inInset();
128                 else if (inset_id >= 0)
129                         in = bv->buffer()->getInsetFromID(inset_id);
130                 for (size_t i = 0, n = undo.pars.size(); i < n; ++i)
131                         undo.pars[i]->setInsetOwner(in);
132         }
133
134
135         // quick hack to make the common case work
136         if (undo.pars.size() == 1 && boost::next(first) == after) {
137         //      first = *undo.pars[0];
138                 lyxerr << "could use special case...\n";
139         }
140
141 #if 0
142         // remove stuff between first and after
143         plist->erase(first, after);
144         // re-create first
145         if (before == null) {
146                 // if there's no before take the beginning of parlist.
147                 first = plist->begin();
148         } else {
149                 first = *before;
150                 ++first;
151         }
152
153 #endif
154
155         // inset saved paragraphs
156         for (size_t i = 0, n = undo.pars.size(); i < n; ++i) {
157                 lyxerr << " inserting par " << undo.pars[i]->id() << "\n";
158                 lyxerr << " inserting plist: " << plist << "\n";
159                 //plist->insert(first, new Paragraph(*undo.pars[i], true));
160                 /*
161                         // A memory optimization for edit:
162                         // Only layout information
163                         // is stored in the undo. So restore
164                         // the text informations.
165                         if (undo.kind == Undo::EDIT) {
166                                 undo.pars[par]->setContentsFromPar(*deletelist.back());
167                                 ++par;
168                         }
169                 */
170         }
171
172         // Set the cursor for redoing
173         // if we have a par before the first.
174         if (before != null) {
175                 Inset * it = before->inInset();
176                 if (it)
177                         it->getLyXText(bv)->setCursorIntern(*before, 0);
178                 else
179                         bv->text->setCursorIntern(*before, 0);
180         }
181
182         UpdatableInset * it = 0;
183         if (first != plist->begin())
184                 it = static_cast<UpdatableInset*>(first->inInset());
185
186         LyXText * text = it ? it->getLyXText(bv) : bv->text;
187
188         text->redoParagraphs(text->cursor, plist->end());
189
190         ParIterator tmppar = bv->buffer()->getParFromID(inset_id);
191
192         if (tmppar != null) {
193                 LyXText * t;
194                 Inset * it = tmppar->inInset();
195                 if (it) {
196                         FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
197                         it->localDispatch(cmd);
198                         t = it->getLyXText(bv);
199                 } else {
200                         t = bv->text;
201                 }
202                 t->setCursorIntern(*tmppar, undo.cursor_pos);
203                 // Clear any selection and set the selection
204                 // cursor for an evt. new selection.
205                 t->clearSelection();
206                 t->selection.cursor = t->cursor;
207                 t->updateCounters();
208         }
209
210         if (it) {
211                 bv->fitCursor();
212                 bv->updateInset(it);
213                 bv->text->setCursorIntern(bv->text->cursor.par(),
214                                           bv->text->cursor.pos());
215         }
216
217
218         finishUndo();
219         bv->text->postPaint(0);
220
221         return true;
222 }
223
224
225 bool createUndo(BufferView * bv, Undo::undo_kind kind,
226         ParagraphList::iterator itfirst, ParagraphList::iterator itlast,
227         shared_ptr<Undo> & u)
228 {
229         Buffer * b = bv->buffer();
230
231         ParIterator null    = b->par_iterator_end();
232         ParIterator prev    = null;
233         ParIterator before  = null;
234         ParIterator first   = null;
235         ParIterator last    = null;
236         ParIterator behind  = null;
237
238         int const first_id  = itfirst->id();
239         int const last_id   = itlast->id();
240
241         for (ParIterator it = b->par_iterator_begin(); it != null; ++it) {
242                 if ((*it)->id() == first_id) {
243                         first = it;
244                         before = prev;
245                 }
246                 if ((*it)->id() == last_id) {
247                         last = it;
248                         behind = last;
249                         ++behind;
250                 }
251                 prev = it;
252         }
253
254         if (last == null)
255                 last = first;
256
257         int const before_id = (before == null) ? -1 : before->id();
258         int const behind_id = (behind == null) ? -1 : behind->id();
259         int inset_id        = (first->inInset()) ? first->inInset()->id() : -1;
260
261         lyxerr << "\nbefore_id: " << before_id << "\n";
262         lyxerr << "first_id:  " << first_id  << "\n";
263         lyxerr << "last_id:   " << last_id   << "\n";
264         lyxerr << "behind_id: " << behind_id << "\n";
265         lyxerr << "inset_id:  " << inset_id  << "\n";
266
267         ParagraphList * plist = 0;
268         if (first != null)
269                 plist = &first.plist();
270         else if (behind != null)
271                 plist = &behind.plist();
272         else if (!plist) {
273                 lyxerr << "plist from buffer (should this happen?)\n";
274                 plist = &b->paragraphs;
275         }
276
277         // Undo::EDIT and Undo::FINISH are
278         // always finished. (no overlapping there)
279         // overlapping only with insert and delete inside one paragraph:
280         // Nobody wants all removed  character
281         // appear one by one when undoing.
282         // EDIT is special since only layout information, not the
283         // contents of a paragaph are stored.
284         if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) {
285                 // Check whether storing is needed.
286                 if (!b->undostack.empty() &&
287                     b->undostack.top()->kind == kind &&
288                     b->undostack.top()->number_of_before_par == before_id &&
289                     b->undostack.top()->number_of_behind_par == behind_id) {
290                         // No undo needed.
291                         return false;
292                 }
293         }
294
295         // Create a new Undo.
296 #warning FIXME Why is this a vector and not a ParagraphList?
297         std::vector<Paragraph *> undo_pars;
298
299         for (ParagraphList::iterator it = *first; it != *last; ++it) {
300                 int id = it->id();
301                 Paragraph * tmp = new Paragraph(*it);
302                 tmp->id(id);
303                 undo_pars.push_back(tmp);
304         }
305
306         int const id = last->id();
307         Paragraph * tmp = new Paragraph(**last);
308         tmp->id(id);
309         undo_pars.push_back(tmp);
310
311         // A memory optimization: Just store the layout
312         // information when only edit.
313 #warning Waste...
314         //if (kind == Undo::EDIT)
315         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
316         //              undo_pars[i]->clearContents();
317
318         int cursor_par = undoCursor(bv).par()->id();
319         int cursor_pos = undoCursor(bv).pos();
320
321         u.reset(new Undo(kind, inset_id,
322                 before_id, behind_id, cursor_par, cursor_pos, undo_pars));
323
324         undo_finished = false;
325         return true;
326 }
327
328
329 // Returns false if no undo possible.
330 bool textUndoOrRedo(BufferView * bv,
331         limited_stack<boost::shared_ptr<Undo> > & stack,
332                     limited_stack<boost::shared_ptr<Undo> > & /*otherstack*/)
333 {
334         //Buffer * b = bv->buffer();
335
336         if (stack.empty()) {
337                 finishNoUndo(bv);
338                 return false;
339         }
340
341         shared_ptr<Undo> undo = stack.top();
342         stack.pop();
343         finishUndo();
344
345         if (!undo_frozen) {
346 /*
347                 ParIterator p = b->getParFromID(undo->number_of_before_par);
348                 bool ok = false;
349                 ParagraphList::iterator first;
350                 // default constructed?
351                 ParIterator const end = b->par_iterator_end();
352                 if (p != end) {
353                         first = p.par();
354                         if (first->next())
355                                 first = first->next();
356                 } else
357                         first = undoParagraphs(bv, undo->number_of_inset_id)->begin();
358                 if (first) {
359                         shared_ptr<Undo> u;
360                         ParIterator behind = b->getParFromID(undo->number_of_behind_par);
361                         if (createUndo(bv, undo->kind, first, behind.par(), u))
362                                 otherstack.push(u);
363                 }
364 */
365         }
366
367         // Now we can unlock the inset for saftey because the inset
368         // pointer could be changed during the undo-function. Anyway
369         // if needed we have to lock the right inset/position if this
370         // is requested.
371         freezeUndo();
372         bv->unlockInset(bv->theLockingInset());
373         bool const ret = textHandleUndo(bv, *undo.get());
374         unFreezeUndo();
375         return ret;
376 }
377
378 } // namespace anon
379
380
381 void finishUndo()
382 {
383         // Makes sure the next operation will be stored.
384         undo_finished = true;
385 }
386
387
388 void freezeUndo()
389 {
390         // This is dangerous and for internal use only.
391         undo_frozen = true;
392 }
393
394
395 void unFreezeUndo()
396 {
397         // This is dangerous and for internal use only.
398         undo_frozen = false;
399 }
400
401
402 bool textUndo(BufferView * bv)
403 {
404         return textUndoOrRedo(bv, bv->buffer()->undostack,
405                               bv->buffer()->redostack);
406 }
407
408
409 bool textRedo(BufferView * bv)
410 {
411         return textUndoOrRedo(bv, bv->buffer()->redostack,
412                               bv->buffer()->undostack);
413 }
414
415
416 void setUndo(BufferView * bv, Undo::undo_kind kind,
417              ParagraphList::iterator first)
418 {
419         setUndo(bv, kind, first, first);
420 }
421
422
423 void setUndo(BufferView * bv, Undo::undo_kind kind,
424              ParagraphList::iterator first, ParagraphList::iterator last)
425 {
426 #warning DISABLED
427         //return;
428         if (!undo_frozen) {
429                 shared_ptr<Undo> u;
430                 if (createUndo(bv, kind, first, last, u))
431                         bv->buffer()->undostack.push(u);
432                 bv->buffer()->redostack.clear();
433         }
434 }
435
436
437 void setCursorParUndo(BufferView * bv)
438 {
439         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
440 }