]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
bit of undo
[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
24 /// The flag used by FinishUndo().
25 bool undo_finished;
26 /// Whether actions are not added to the undo stacks.
27 bool undo_frozen;
28
29 namespace {
30
31
32 /**
33  * Finish the undo operation in the case there was no entry
34  * on the stack to perform.
35  */
36 void finishNoUndo(BufferView * bv)
37 {
38         freezeUndo();
39         bv->unlockInset(bv->theLockingInset());
40         finishUndo();
41         bv->text->postPaint(0);
42         unFreezeUndo();
43 }
44
45
46 // Returns false if no undo possible.
47 bool textHandleUndo(BufferView * bv, Undo & undo)
48 {
49         Buffer * buf = bv->buffer();
50
51         ParIterator const before = buf->getParFromID(undo.number_of_before_par);
52         ParIterator const behind = buf->getParFromID(undo.number_of_behind_par);
53         ParIterator const null   = buf->par_iterator_end();
54
55         int const before_id = (before == null) ? -1 : before->id();
56         int const behind_id = (behind == null) ? -1 : behind->id();
57         int const inset_id  = undo.number_of_inset_id;
58
59         Inset * inset = bv->buffer()->getInsetFromID(inset_id);
60         LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
61
62         ParagraphList * plist = &bv->text->ownerParagraphs();
63         if (inset) {
64                 ParagraphList * tmp = inset->getParagraphs(0);
65                 if (tmp && !tmp->empty())
66                         plist = tmp;
67         }
68
69         ParagraphList::iterator first;
70         if (before == null) {
71                 // if there's no before take the beginning of parlist.
72                 first = plist->begin();
73                 text->setCursorIntern(plist->begin(), 0);
74         } else {
75                 first = *before;
76                 ++first;
77         }
78         int const first_id  = first->id();
79
80         lyxerr << "\nhandle: before_id: " << before_id << "\n";
81         lyxerr << "handle: first_id:  " << first_id  << "\n";
82         lyxerr << "handle: behind_id: " << behind_id << "\n";
83         lyxerr << "handle: inset_id: " << inset_id << "\n";
84
85         // Set the right(new) inset-owner of the paragraph if there is any.
86         UpdatableInset * in = 0;
87         if (before != null)
88                 in = before->inInset();
89         else if (inset_id >= 0) {
90                 Inset * inset = bv->buffer()->getInsetFromID(inset_id);
91                 in = static_cast<UpdatableInset *>(inset);
92         }
93         ParagraphList::iterator pit = undo.pars.begin();
94         ParagraphList::iterator end = undo.pars.end();
95         for ( ; pit != end; ++pit)
96                 pit->setInsetOwner(in);
97         lyxerr << "in: " << in << "\n";
98         lyxerr << "undo.pars.size(): " << undo.pars.size() << "\n";
99
100         // remove stuff between first and behind
101         if (behind == null) 
102                 plist->erase(first, plist->end());
103         else
104                 plist->erase(first, *behind);
105         lyxerr << "after erase\n";
106
107         // re-create first
108         if (before == null) {
109                 // if there's no before take the beginning of parlist.
110                 lyxerr << "no 'before'\n";
111                 first = plist->begin();
112         } else {
113                 lyxerr << "have 'before'\n";
114                 first = *before;
115                 ++first;
116         }
117
118
119         // inset saved paragraphs
120         lyxerr << "undo.pars.size(): " << undo.pars.size() << "\n";
121         plist->insert(first, undo.pars.begin(), undo.pars.end());
122         lyxerr << "after insert\n";
123         /*
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                         undo.pars[par]->setContentsFromPar(*deletelist.back());
130                         ++par;
131                 }
132         */
133
134         // Set the cursor for redoing
135         // if we have a par before the first.
136         if (before != null) {
137                 Inset * it = before->inInset();
138                 LyXText * text = it ? it->getLyXText(bv) : bv->text;
139                 text->setCursorIntern(*before, 0);
140         }
141
142         UpdatableInset * it = 0;
143         if (first != plist->end())
144                 it = first->inInset();
145         lyxerr << "it: " << it << "\n";
146
147
148         text->redoParagraphs(text->cursor, plist->end());
149
150         ParIterator tmppar = bv->buffer()->getParFromID(inset_id);
151
152         if (tmppar != null) {
153                 lyxerr << "tmppar: " << tmppar->id() << "\n";
154                 LyXText * t;
155                 Inset * it = tmppar->inInset();
156                 if (it) {
157                         FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
158                         it->localDispatch(cmd);
159                         t = it->getLyXText(bv);
160                 } else {
161                         t = bv->text;
162                 }
163                 t->setCursorIntern(*tmppar, undo.cursor_pos);
164                 // Clear any selection and set the selection
165                 // cursor for an evt. new selection.
166                 t->clearSelection();
167                 t->selection.cursor = t->cursor;
168                 t->updateCounters();
169         } else {
170                 lyxerr << "tmppar == null \n";
171         }
172
173         if (it) {
174                 lyxerr << "fit cursor...\n";
175                 bv->fitCursor();
176                 bv->updateInset(it);
177                 bv->text->setCursorIntern(bv->text->cursor.par(),
178                                           bv->text->cursor.pos());
179         }
180
181         finishUndo();
182         bv->text->postPaint(0);
183
184         lyxerr << "finished  textHandleUndo...\n";
185         return true;
186 }
187
188
189 void createUndo(BufferView * bv, Undo::undo_kind kind,
190         int first_id, int last_id,
191         limited_stack<Undo> & stack)
192 {
193         Buffer * buf = bv->buffer();
194
195         ParIterator null    = buf->par_iterator_end();
196         ParIterator prev    = null;
197         ParIterator before  = null;
198         ParIterator first   = null;
199         ParIterator last    = null;
200         ParIterator behind  = null;
201
202         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it) {
203                 if (it->id() == first_id) {
204                         first = it;
205                         before = prev;
206                 }
207                 if (it->id() == last_id) {
208                         last = it;
209                         behind = last;
210                         ++behind;
211                 }
212                 prev = it;
213         }
214
215         if (last == null)
216                 last = first;
217
218         int const before_id = (before == null) ? -1 : before->id();
219         int const behind_id = (behind == null) ? -1 : behind->id();
220         int inset_id        = (first->inInset()) ? first->inInset()->id() : -1;
221
222         lyxerr << "\ncreate: before_id: " << before_id << "\n";
223         lyxerr << "create: first_id:  " << first_id  << "\n";
224         lyxerr << "create: last_id:   " << last_id   << "\n";
225         lyxerr << "create: behind_id: " << behind_id << "\n";
226         lyxerr << "create: inset_id:  " << inset_id  << "\n";
227         lyxerr << "create: kind:  " << kind  << "\n";
228
229         ParagraphList * plist = 0;
230         if (first != null)
231                 plist = &first.plist();
232         else if (behind != null)
233                 plist = &behind.plist();
234         else if (!plist) {
235                 lyxerr << "plist from buffer (should this happen?)\n";
236                 plist = &buf->paragraphs;
237         }
238
239         // Undo::EDIT and Undo::FINISH are
240         // always finished. (no overlapping there)
241         // overlapping only with insert and delete inside one paragraph:
242         // Nobody wants all removed  character
243         // appear one by one when undoing.
244         // EDIT is special since only layout information, not the
245         // contents of a paragaph are stored.
246         if (!undo_finished && kind != Undo::EDIT && kind != Undo::FINISH) {
247                 // Check whether storing is needed.
248                 if (!buf->undostack.empty() &&
249                     buf->undostack.top().kind == kind &&
250                     buf->undostack.top().number_of_before_par == before_id &&
251                     buf->undostack.top().number_of_behind_par == behind_id) {
252                         // No undo needed.
253                         return;
254                 }
255         }
256
257         // Create a new Undo.
258         LyXCursor const & cur = bv->theLockingInset() ?
259                         bv->theLockingInset()->cursor(bv) : bv->text->cursor;
260
261         stack.push(Undo(kind, inset_id,
262                 before_id, behind_id, cur.par()->id(), cur.pos(), ParagraphList()));
263
264         ParagraphList & undo_pars = stack.top().pars;
265
266         for (ParagraphList::iterator it = *first; it != *last; ++it) {
267                 undo_pars.push_back(*it);
268                 undo_pars.back().id(it->id());
269         }
270         undo_pars.push_back(**last);
271         undo_pars.back().id(last->id());
272
273         // A memory optimization: Just store the layout
274         // information when only edit.
275 #warning Waste...
276         //if (kind == Undo::EDIT)
277         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
278         //              undo_pars[i]->clearContents();
279
280         undo_finished = false;
281 }
282
283
284 // Returns false if no undo possible.
285 bool textUndoOrRedo(BufferView * bv,
286         limited_stack<Undo> & stack,
287         limited_stack<Undo> & /*otherstack*/)
288 {
289         if (stack.empty()) {
290                 finishNoUndo(bv);
291                 return false;
292         }
293
294         Undo undo = stack.top();
295         stack.pop();
296         finishUndo();
297
298 /*
299         if (!undo_frozen) {
300                 Buffer * buf = bv->buffer();
301                 ParIterator p = buf->getParFromID(undo->number_of_before_par);
302                 ParIterator const end = buf->par_iterator_end();
303                 bool ok = false;
304                 ParagraphList::iterator first;
305                 // default constructed?
306                 if (p != end) {
307                         first = p.par();
308                         if (first->next())
309                                 first = first->next();
310                 } else {
311                         // Set first to the very first Paragraph depending of where
312                         // we are so it will return the first paragraph of the buffer or the
313                         // first paragraph of the textinset we're in.
314                         first = bv->text->ownerParagraphs()->begin();
315                         Inset * inset = bv->buffer()->getInsetFromID(inset_id);
316                         if (inset) {
317                                 ParagraphList * result = inset->getParagraphs(0);
318                                 if (result && !result->empty())
319                                         first = result->begin();
320                         }
321                 }
322                 if (ok) {
323                         ParIterator behind = buf->getParFromID(undo.number_of_behind_par);
324                         createUndo(bv, undo.kind, first, behind.par(), otherstack);
325                 }
326         }
327 */
328
329         // Now we can unlock the inset for saftey because the inset
330         // pointer could be changed during the undo-function. Anyway
331         // if needed we have to lock the right inset/position if this
332         // is requested.
333         freezeUndo();
334         bv->unlockInset(bv->theLockingInset());
335         bool const ret = textHandleUndo(bv, undo);
336         unFreezeUndo();
337         return ret;
338 }
339
340 } // namespace anon
341
342
343 void finishUndo()
344 {
345         // Makes sure the next operation will be stored.
346         undo_finished = true;
347 }
348
349
350 void freezeUndo()
351 {
352         // This is dangerous and for internal use only.
353         undo_frozen = true;
354 }
355
356
357 void unFreezeUndo()
358 {
359         // This is dangerous and for internal use only.
360         undo_frozen = false;
361 }
362
363
364 bool textUndo(BufferView * bv)
365 {
366         return textUndoOrRedo(bv, bv->buffer()->undostack,
367                               bv->buffer()->redostack);
368 }
369
370
371 bool textRedo(BufferView * bv)
372 {
373         return textUndoOrRedo(bv, bv->buffer()->redostack,
374                               bv->buffer()->undostack);
375 }
376
377
378 void setUndo(BufferView * bv, Undo::undo_kind kind,
379              ParagraphList::iterator first)
380 {
381         setUndo(bv, kind, first, first);
382 }
383
384
385 void setUndo(BufferView * bv, Undo::undo_kind kind,
386              ParagraphList::iterator first, ParagraphList::iterator last)
387 {
388         if (!undo_frozen) {
389                 createUndo(bv, kind, first->id(), last->id(), bv->buffer()->undostack);
390                 bv->buffer()->redostack.clear();
391         }
392 }
393
394
395 void setCursorParUndo(BufferView * bv)
396 {
397         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
398 }