]> git.lyx.org Git - lyx.git/blob - src/undo_funcs.C
move some selection related stuff over to textcursor.C
[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         ParagraphList * plist = &buf->paragraphs;
52 /*
53         ParIterator null = buf->par_iterator_end();
54
55         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it)
56                 if (it.plist().id() == undo.plist_id) {
57                         plist = &it.plist();
58                         break;
59                 }
60 */
61
62         // Set the right(new) inset-owner of the paragraph if there is any.
63         UpdatableInset * inset =
64                 static_cast<UpdatableInset *>(buf->getInsetFromID(undo.inset_id));
65         
66         ParagraphList::iterator pit = undo.pars.begin();
67         ParagraphList::iterator end = undo.pars.end();
68         for ( ; pit != end; ++pit)
69                 pit->setInsetOwner(inset);
70
71         //lyxerr << "\nhandle: inset_id: " << undo.inset_id << "\n";
72         //lyxerr << "handle: inset: " << inset << "\n";
73         //lyxerr << "handle: plist_id: " << undo.plist_id << "\n";
74         lyxerr << "handle: undo.pars.size(): " << undo.pars.size() << "\n";
75         lyxerr << "handle: first_offset:  " << undo.first_par_offset  << "\n";
76         lyxerr << "handle: last_offset: " << undo.last_par_offset << "\n";
77
78         // remove stuff between first and behind
79         {
80                 ParagraphList::iterator first = plist->begin();
81                 advance(first, undo.first_par_offset);
82                 ParagraphList::iterator last = plist->begin();
83                 advance(last, plist->size() - undo.last_par_offset);
84                 lyxerr << "handle: first_id:  " << first->id()  << "\n";
85                 lyxerr << "handle: last_id: " << last->id() << "\n";
86                 lyxerr << "handle: remove: " << distance(first, last) + 1 << " pars\n";
87                 plist->erase(first, ++last);
88                 lyxerr << "handle: after remove\n";
89         }
90
91         // re-insert old stuff
92         {
93                 ParagraphList::iterator first = plist->begin();
94                 advance(first, undo.first_par_offset);
95                 lyxerr << "handle: plist->size: " << plist->size() << "\n";
96                 lyxerr << "handle: offset: " << undo.first_par_offset << "\n";
97                 plist->insert(first, undo.pars.begin(), undo.pars.end());
98                 lyxerr << "handle: after insert\n";
99         }
100
101         /*
102                 // A memory optimization for edit:
103                 // Only layout information
104                 // is stored in the undo. So restore
105                 // the text informations.
106                 if (undo.kind == Undo::EDIT) {
107                         undo.pars[par]->setContentsFromPar(*deletelist.back());
108                         ++par;
109                 }
110         */
111
112         // redo Paragraphs  (should be handled outside undo...)
113         {
114                 //LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
115                 LyXText * text = bv->text;
116                 lyxerr << "handle: text: " << text << "\n";
117                 if (undo.first_par_offset) {
118                         ParagraphList::iterator redo = plist->begin();
119                         lyxerr << "handle: 1\n";
120                         advance(redo, undo.first_par_offset);
121                         lyxerr << "handle: 2\n";
122                         text->setCursorIntern(plist->begin(), 0);
123                 }
124                 lyxerr << "handle: 3\n";
125                 text->redoParagraphs(text->cursor, plist->end());
126                 lyxerr << "handle: after redo\n";
127
128                 if (inset) {
129                         FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
130                         inset->localDispatch(cmd);
131                 }
132         }
133         
134         if (inset) {
135                 lyxerr << "fit cursor...\n";
136                 bv->fitCursor();
137                 bv->updateInset(inset);
138         }
139
140         // set cursor
141         {
142                 LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
143                 ParagraphList::iterator cursor = text->ownerParagraphs().begin();
144                 advance(cursor, undo.cursor_par_offset);
145                 text->setCursorIntern(cursor, undo.cursor_pos);
146                 // Clear any selection and set the selection
147                 // cursor for an evt. new selection.
148                 text->clearSelection();
149                 text->selection.cursor = text->cursor;
150                 text->updateCounters();
151                 lyxerr << "after setCursor\n";
152         }
153
154
155         finishUndo();
156         bv->text->postPaint(0);
157
158         lyxerr << "finished  textHandleUndo...\n";
159         return true;
160 }
161
162
163 void createUndo(BufferView * bv, Undo::undo_kind kind,
164         ParagraphList::iterator first, ParagraphList::iterator last,
165         limited_stack<Undo> & stack)
166 {
167         Buffer * buf = bv->buffer();
168
169         ParagraphList * plist = 0;
170         ParIterator null = buf->par_iterator_end();
171
172         lyxerr << "\n";
173
174         UpdatableInset * inset = first->inInset();
175         LyXText * text = inset ? inset->getLyXText(bv) : bv->text;
176         int const inset_id = inset ? inset->id() : -1;
177
178 #if 0
179         // this is what we'd like to have in the end for small grained undo
180         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it) {
181                 if (it->id() == first->id()) {
182                         plist = &it.plist();
183                         break;
184                 }
185         }
186
187 #else
188
189         // and that's the big stick we wield now
190         lyxerr << "create: first_id orig:   " << first->id()  << "\n";
191         lyxerr << "create: last_id orig:    " << last->id()   << "\n";
192         plist = &buf->paragraphs;
193         // this is what we'd like to have in the end for small grained undo
194         for (ParIterator it = buf->par_iterator_begin(); it != null; ++it) {
195                 if (it->id() == first->id()) 
196                         first = it.outerPar(); 
197                 if (it->id() == last->id())
198                         last = it.outerPar(); 
199         }
200
201 #endif
202
203         int const first_offset = std::distance(plist->begin(), first);
204         int const last_offset  = std::distance(last, plist->end());
205
206         if (last == plist->end()) {
207                 lyxerr << "*** createUndo: last == end  should not happen\n";
208         }
209
210
211         // Undo::EDIT and Undo::FINISH are
212         // always finished. (no overlapping there)
213         // overlapping only with insert and delete inside one paragraph:
214         // Nobody wants all removed  character
215         // appear one by one when undoing.
216         // EDIT is special since only layout information, not the
217         // contents of a paragaph are stored.
218         if (!undo_finished && kind != Undo::EDIT && kind != Undo::FINISH) {
219                 // Check whether storing is needed.
220                 if (!buf->undostack.empty() &&
221                     buf->undostack.top().kind == kind &&
222                     buf->undostack.top().first_par_offset == first_offset &&
223                     buf->undostack.top().last_par_offset == last_offset) {
224                         // No undo needed.
225                         return;
226                 }
227         }
228
229         // Create a new Undo.
230         int const cursor_offset = std::distance
231                 (text->ownerParagraphs().begin(), text->cursor.par());
232
233         //lyxerr << "create: plist_id:      " << plist->id()  << "\n";
234         //lyxerr << "create: inset_id:      " << inset_id  << "\n";
235         lyxerr << "create: first_id:      " << first->id()  << "\n";
236         lyxerr << "create: last_id:       " << last->id()   << "\n";
237         lyxerr << "create: first_offset:  " << first_offset  << "\n";
238         lyxerr << "create: last_offset:   " << last_offset   << "\n";
239         lyxerr << "create: cursor_offset: " << cursor_offset   << "\n";
240         lyxerr << "create: cursor_pos:    " << text->cursor.pos() << "\n";
241
242         stack.push(Undo(kind, inset_id, 0, //plist->id(),
243                 first_offset, last_offset,
244                 cursor_offset, text->cursor.pos(),
245                 ParagraphList()));
246
247         ParagraphList & undo_pars = stack.top().pars;
248
249         for (ParagraphList::iterator it = first; it != last; ++it) {
250                 undo_pars.push_back(*it);
251                 undo_pars.back().id(it->id());
252         }
253         undo_pars.push_back(*last);
254         undo_pars.back().id(last->id());
255
256         // A memory optimization: Just store the layout
257         // information when only edit.
258 #warning Waste...
259         //if (kind == Undo::EDIT)
260         //      for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
261         //              undo_pars[i]->clearContents();
262
263         undo_finished = false;
264 }
265
266
267 // Returns false if no undo possible.
268 bool textUndoOrRedo(BufferView * bv,
269         limited_stack<Undo> & stack,
270         limited_stack<Undo> & otherstack)
271 {
272         if (stack.empty()) {
273                 finishNoUndo(bv);
274                 return false;
275         }
276
277         Undo undo = stack.top();
278         stack.pop();
279         finishUndo();
280
281         if (!undo_frozen) {
282                 otherstack.push(undo);
283                 otherstack.top().pars.clear();
284                 Buffer * buf = bv->buffer();
285                 ParagraphList & plist = buf->paragraphs;
286                 lyxerr << "\nredo: first: " << undo.first_par_offset << "\n";
287                 lyxerr << "redo: last:  " << undo.last_par_offset << "\n";
288                 lyxerr << "redo: size:  " << plist.size() << "\n";
289                 if (undo.first_par_offset + undo.last_par_offset <= int(plist.size())) {
290                         ParagraphList::iterator first = plist.begin();
291                         advance(first, undo.first_par_offset);
292                         ParagraphList::iterator last = plist.begin();
293                         advance(last, plist.size() - undo.last_par_offset + 1);
294                         otherstack.top().pars.insert(otherstack.top().pars.begin(), first, last);
295                 }
296         }
297
298         // Now we can unlock the inset for safety because the inset
299         // pointer could be changed during the undo-function. Anyway
300         // if needed we have to lock the right inset/position if this
301         // is requested.
302         freezeUndo();
303         bv->unlockInset(bv->theLockingInset());
304         bool const ret = textHandleUndo(bv, undo);
305         unFreezeUndo();
306         return ret;
307 }
308
309 } // namespace anon
310
311
312 void finishUndo()
313 {
314         // Makes sure the next operation will be stored.
315         undo_finished = true;
316 }
317
318
319 void freezeUndo()
320 {
321         // This is dangerous and for internal use only.
322         undo_frozen = true;
323 }
324
325
326 void unFreezeUndo()
327 {
328         // This is dangerous and for internal use only.
329         undo_frozen = false;
330 }
331
332
333 bool textUndo(BufferView * bv)
334 {
335         return textUndoOrRedo(bv, bv->buffer()->undostack,
336                               bv->buffer()->redostack);
337 }
338
339
340 bool textRedo(BufferView * bv)
341 {
342         return textUndoOrRedo(bv, bv->buffer()->redostack,
343                               bv->buffer()->undostack);
344 }
345
346
347 void setUndo(BufferView * bv, Undo::undo_kind kind)
348 {
349         setUndo(bv, kind, bv->text->cursor.par());
350 }
351
352
353 void setUndo(BufferView * bv, Undo::undo_kind kind,
354              ParagraphList::iterator first)
355 {
356         setUndo(bv, kind, first, first);
357 }
358
359
360 void setUndo(BufferView * bv, Undo::undo_kind kind,
361              ParagraphList::iterator first, ParagraphList::iterator last)
362 {
363         if (!undo_frozen) {
364                 createUndo(bv, kind, first, last, bv->buffer()->undostack);
365                 bv->buffer()->redostack.clear();
366         }
367 }
368
369
370 void setCursorParUndo(BufferView * bv)
371 {
372         setUndo(bv, Undo::FINISH, bv->text->cursor.par());
373 }