]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
most of textHandleUndo
[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         std::vector<Paragraph *> undo_pars;
297
298         for (ParagraphList::iterator it = *first; it != *last; ++it) 
299                 undo_pars.push_back(new Paragraph(*it, true));
300         undo_pars.push_back(new Paragraph(**last, true));
301
302         // A memory optimization: Just store the layout
303         // information when only edit.
304 #warning Waste...
305         //if (kind == Undo::EDIT)
306         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
307         //              undo_pars[i]->clearContents();
308
309         int cursor_par = undoCursor(bv).par()->id();
310         int cursor_pos = undoCursor(bv).pos();
311
312         u.reset(new Undo(kind, inset_id,
313                 before_id, behind_id, cursor_par, cursor_pos, undo_pars));
314
315         undo_finished = false;
316         return true;
317 }
318
319
320 // Returns false if no undo possible.
321 bool textUndoOrRedo(BufferView * bv,
322         limited_stack<boost::shared_ptr<Undo> > & stack,
323                     limited_stack<boost::shared_ptr<Undo> > & /*otherstack*/)
324 {
325         //Buffer * b = bv->buffer();
326
327         if (stack.empty()) {
328                 finishNoUndo(bv);
329                 return false;
330         }
331
332         shared_ptr<Undo> undo = stack.top();
333         stack.pop();
334         finishUndo();
335
336         if (!undo_frozen) {
337 /*
338                 ParIterator p = b->getParFromID(undo->number_of_before_par);
339                 bool ok = false;
340                 ParagraphList::iterator first;
341                 // default constructed?
342                 ParIterator const end = b->par_iterator_end();
343                 if (p != end) {
344                         first = p.par();
345                         if (first->next())
346                                 first = first->next();
347                 } else
348                         first = undoParagraphs(bv, undo->number_of_inset_id)->begin();
349                 if (first) {
350                         shared_ptr<Undo> u;
351                         ParIterator behind = b->getParFromID(undo->number_of_behind_par);
352                         if (createUndo(bv, undo->kind, first, behind.par(), u))
353                                 otherstack.push(u);
354                 }
355 */
356         }
357
358         // Now we can unlock the inset for saftey because the inset
359         // pointer could be changed during the undo-function. Anyway
360         // if needed we have to lock the right inset/position if this
361         // is requested.
362         freezeUndo();
363         bv->unlockInset(bv->theLockingInset());
364         bool const ret = textHandleUndo(bv, *undo.get());
365         unFreezeUndo();
366         return ret;
367 }
368
369 } // namespace anon
370
371
372 void finishUndo()
373 {
374         // Makes sure the next operation will be stored.
375         undo_finished = true;
376 }
377
378
379 void freezeUndo()
380 {
381         // This is dangerous and for internal use only.
382         undo_frozen = true;
383 }
384
385
386 void unFreezeUndo()
387 {
388         // This is dangerous and for internal use only.
389         undo_frozen = false;
390 }
391
392
393 bool textUndo(BufferView * bv)
394 {
395         return textUndoOrRedo(bv, bv->buffer()->undostack,
396                               bv->buffer()->redostack);
397 }
398
399
400 bool textRedo(BufferView * bv)
401 {
402         return textUndoOrRedo(bv, bv->buffer()->redostack,
403                               bv->buffer()->undostack);
404 }
405
406
407 void setUndo(BufferView * bv, Undo::undo_kind kind,
408              ParagraphList::iterator first)
409 {
410         setUndo(bv, kind, first, first);
411 }
412
413
414 void setUndo(BufferView * bv, Undo::undo_kind kind,
415              ParagraphList::iterator first, ParagraphList::iterator last)
416 {
417 #warning DISABLED
418         //return;
419         if (!undo_frozen) {
420                 shared_ptr<Undo> u;
421                 if (createUndo(bv, kind, first, last, u))
422                         bv->buffer()->undostack.push(u);
423                 bv->buffer()->redostack.clear();
424         }
425 }
426
427
428 void setCursorParUndo(BufferView * bv)
429 {
430         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
431 }