]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
"Inter-word Space"
[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         finishUndo();
218         bv->text->postPaint(0);
219
220         return true;
221 }
222
223
224 bool createUndo(BufferView * bv, Undo::undo_kind kind,
225         int first_id, int last_id, shared_ptr<Undo> & u)
226 {
227         Buffer * b = bv->buffer();
228
229         ParIterator null    = b->par_iterator_end();
230         ParIterator prev    = null;
231         ParIterator before  = null;
232         ParIterator first   = null;
233         ParIterator last    = null;
234         ParIterator behind  = null;
235
236         for (ParIterator it = b->par_iterator_begin(); it != null; ++it) {
237                 if ((*it)->id() == first_id) {
238                         first = it;
239                         before = prev;
240                 }
241                 if ((*it)->id() == last_id) {
242                         last = it;
243                         behind = last;
244                         ++behind;
245                 }
246                 prev = it;
247         }
248
249         if (last == null)
250                 last = first;
251
252         int const before_id = (before == null) ? -1 : before->id();
253         int const behind_id = (behind == null) ? -1 : behind->id();
254         int inset_id        = (first->inInset()) ? first->inInset()->id() : -1;
255
256         lyxerr << "\nbefore_id: " << before_id << "\n";
257         lyxerr << "first_id:  " << first_id  << "\n";
258         lyxerr << "last_id:   " << last_id   << "\n";
259         lyxerr << "behind_id: " << behind_id << "\n";
260         lyxerr << "inset_id:  " << inset_id  << "\n";
261
262         ParagraphList * plist = 0;
263         if (first != null)
264                 plist = &first.plist();
265         else if (behind != null)
266                 plist = &behind.plist();
267         else if (!plist) {
268                 lyxerr << "plist from buffer (should this happen?)\n";
269                 plist = &b->paragraphs;
270         }
271
272         // Undo::EDIT and Undo::FINISH are
273         // always finished. (no overlapping there)
274         // overlapping only with insert and delete inside one paragraph:
275         // Nobody wants all removed  character
276         // appear one by one when undoing.
277         // EDIT is special since only layout information, not the
278         // contents of a paragaph are stored.
279         if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) {
280                 // Check whether storing is needed.
281                 if (!b->undostack.empty() &&
282                     b->undostack.top()->kind == kind &&
283                     b->undostack.top()->number_of_before_par == before_id &&
284                     b->undostack.top()->number_of_behind_par == behind_id) {
285                         // No undo needed.
286                         return false;
287                 }
288         }
289
290         // Create a new Undo.
291 #warning FIXME Why is this a vector and not a ParagraphList?
292         std::vector<Paragraph *> undo_pars;
293
294         for (ParagraphList::iterator it = *first; it != *last; ++it) {
295                 int id = it->id();
296                 Paragraph * tmp = new Paragraph(*it);
297                 tmp->id(id);
298                 undo_pars.push_back(tmp);
299         }
300
301         int const id = last->id();
302         Paragraph * tmp = new Paragraph(**last);
303         tmp->id(id);
304         undo_pars.push_back(tmp);
305
306         // A memory optimization: Just store the layout
307         // information when only edit.
308 #warning Waste...
309         //if (kind == Undo::EDIT)
310         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
311         //              undo_pars[i]->clearContents();
312
313         int cursor_par = undoCursor(bv).par()->id();
314         int cursor_pos = undoCursor(bv).pos();
315
316         u.reset(new Undo(kind, inset_id,
317                 before_id, behind_id, cursor_par, cursor_pos, undo_pars));
318
319         undo_finished = false;
320         return true;
321 }
322
323
324 // Returns false if no undo possible.
325 bool textUndoOrRedo(BufferView * bv,
326         limited_stack<boost::shared_ptr<Undo> > & stack,
327                     limited_stack<boost::shared_ptr<Undo> > & /*otherstack*/)
328 {
329         if (stack.empty()) {
330                 finishNoUndo(bv);
331                 return false;
332         }
333
334         shared_ptr<Undo> undo = stack.top();
335         stack.pop();
336         finishUndo();
337
338 /*
339         if (!undo_frozen) {
340                 Buffer * buf = bv->buffer();
341                 ParIterator p = b->getParFromID(undo->number_of_before_par);
342                 ParIterator const end = b->par_iterator_end();
343                 bool ok = false;
344                 ParagraphList::iterator first;
345                 // default constructed?
346                 if (p != end) {
347                         first = p.par();
348                         if (first->next())
349                                 first = first->next();
350                 } else
351                         first = undoParagraphs(bv, undo->number_of_inset_id)->begin();
352                 if (ok) {
353                         shared_ptr<Undo> u;
354                         ParIterator behind = b->getParFromID(undo->number_of_behind_par);
355                         if (createUndo(bv, undo->kind, first, behind.par(), u))
356                                 otherstack.push(u);
357                 }
358         }
359 */
360
361         // Now we can unlock the inset for saftey because the inset
362         // pointer could be changed during the undo-function. Anyway
363         // if needed we have to lock the right inset/position if this
364         // is requested.
365         freezeUndo();
366         bv->unlockInset(bv->theLockingInset());
367         bool const ret = textHandleUndo(bv, *undo.get());
368         unFreezeUndo();
369         return ret;
370 }
371
372 } // namespace anon
373
374
375 void finishUndo()
376 {
377         // Makes sure the next operation will be stored.
378         undo_finished = true;
379 }
380
381
382 void freezeUndo()
383 {
384         // This is dangerous and for internal use only.
385         undo_frozen = true;
386 }
387
388
389 void unFreezeUndo()
390 {
391         // This is dangerous and for internal use only.
392         undo_frozen = false;
393 }
394
395
396 bool textUndo(BufferView * bv)
397 {
398         return textUndoOrRedo(bv, bv->buffer()->undostack,
399                               bv->buffer()->redostack);
400 }
401
402
403 bool textRedo(BufferView * bv)
404 {
405         return textUndoOrRedo(bv, bv->buffer()->redostack,
406                               bv->buffer()->undostack);
407 }
408
409
410 void setUndo(BufferView * bv, Undo::undo_kind kind,
411              ParagraphList::iterator first)
412 {
413         setUndo(bv, kind, first, first);
414 }
415
416
417 void setUndo(BufferView * bv, Undo::undo_kind kind,
418              ParagraphList::iterator first, ParagraphList::iterator last)
419 {
420 #warning DISABLED
421         //return;
422         if (!undo_frozen) {
423                 shared_ptr<Undo> u;
424                 if (createUndo(bv, kind, first->id(), last->id(), u))
425                         bv->buffer()->undostack.push(u);
426                 bv->buffer()->redostack.clear();
427         }
428 }
429
430
431 void setCursorParUndo(BufferView * bv)
432 {
433         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
434 }