1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995-2001 The LyX Team.
8 * ====================================================== */
12 #include "undo_funcs.h"
14 #include "funcrequest.h"
15 #include "BufferView.h"
17 #include "insets/updatableinset.h"
18 #include "insets/insettext.h"
20 #include "support/LAssert.h"
21 #include "iterators.h"
26 using boost::shared_ptr;
29 /// The flag used by FinishUndo().
31 /// Whether actions are not added to the undo stacks.
36 /// Utility to return the cursor.
37 LyXCursor const & undoCursor(BufferView * bv)
39 if (bv->theLockingInset())
40 return bv->theLockingInset()->cursor(bv);
41 return bv->text->cursor;
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.
50 ParagraphList * undoParagraphs(BufferView * bv, int inset_id)
52 Inset * inset = bv->buffer()->getInsetFromID(inset_id);
54 ParagraphList * result = inset->getParagraphs(0);
55 if (result && !result->empty())
58 return &bv->text->ownerParagraphs();
63 * Finish the undo operation in the case there was no entry
64 * on the stack to perform.
66 void finishNoUndo(BufferView * bv)
69 bv->unlockInset(bv->theLockingInset());
71 bv->text->postPaint(0);
76 // Returns false if no undo possible.
77 bool textHandleUndo(BufferView * bv, Undo & undo)
79 Buffer * b = bv->buffer();
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();
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;
89 ParagraphList * plist = undoParagraphs(bv, inset_id);
91 ParagraphList::iterator first;
93 // if there's no before take the beginning of parlist.
94 first = plist->begin();
95 LyXText * t = bv->text;
97 if (Inset * in = bv->buffer()->getInsetFromID(inset_id))
98 t = in->getLyXText(bv);
99 t->setCursorIntern(plist->begin(), 0);
104 int const first_id = first->id();
107 ParagraphList::iterator after;
108 if (behind == null) {
109 // if there's no behind take the end of parlist.
110 after = plist->end();
114 after_id = after->id();
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";
123 // Set the right(new) inset-owner of the paragraph if there is any.
124 if (!undo.pars.empty()) {
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);
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";
142 // remove stuff between first and after
143 plist->erase(first, after);
145 if (before == null) {
146 // if there's no before take the beginning of parlist.
147 first = plist->begin();
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));
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());
172 // Set the cursor for redoing
173 // if we have a par before the first.
174 if (before != null) {
175 Inset * it = before->inInset();
177 it->getLyXText(bv)->setCursorIntern(*before, 0);
179 bv->text->setCursorIntern(*before, 0);
182 UpdatableInset * it = 0;
183 if (first != plist->begin())
184 it = static_cast<UpdatableInset*>(first->inInset());
186 LyXText * text = it ? it->getLyXText(bv) : bv->text;
188 text->redoParagraphs(text->cursor, plist->end());
190 ParIterator tmppar = bv->buffer()->getParFromID(inset_id);
192 if (tmppar != null) {
194 Inset * it = tmppar->inInset();
196 FuncRequest cmd(bv, LFUN_INSET_EDIT, "left");
197 it->localDispatch(cmd);
198 t = it->getLyXText(bv);
202 t->setCursorIntern(*tmppar, undo.cursor_pos);
203 // Clear any selection and set the selection
204 // cursor for an evt. new selection.
206 t->selection.cursor = t->cursor;
213 bv->text->setCursorIntern(bv->text->cursor.par(),
214 bv->text->cursor.pos());
218 bv->text->postPaint(0);
224 bool createUndo(BufferView * bv, Undo::undo_kind kind,
225 int first_id, int last_id, shared_ptr<Undo> & u)
227 Buffer * b = bv->buffer();
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;
236 for (ParIterator it = b->par_iterator_begin(); it != null; ++it) {
237 if ((*it)->id() == first_id) {
241 if ((*it)->id() == last_id) {
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;
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";
262 ParagraphList * plist = 0;
264 plist = &first.plist();
265 else if (behind != null)
266 plist = &behind.plist();
268 lyxerr << "plist from buffer (should this happen?)\n";
269 plist = &b->paragraphs;
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) {
290 // Create a new Undo.
291 #warning FIXME Why is this a vector and not a ParagraphList?
292 std::vector<Paragraph *> undo_pars;
294 for (ParagraphList::iterator it = *first; it != *last; ++it) {
296 Paragraph * tmp = new Paragraph(*it);
298 undo_pars.push_back(tmp);
301 int const id = last->id();
302 Paragraph * tmp = new Paragraph(**last);
304 undo_pars.push_back(tmp);
306 // A memory optimization: Just store the layout
307 // information when only edit.
309 //if (kind == Undo::EDIT)
310 // for (size_t i = 0, n = undo_pars.size(); i < n; ++i)
311 // undo_pars[i]->clearContents();
313 int cursor_par = undoCursor(bv).par()->id();
314 int cursor_pos = undoCursor(bv).pos();
316 u.reset(new Undo(kind, inset_id,
317 before_id, behind_id, cursor_par, cursor_pos, undo_pars));
319 undo_finished = false;
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*/)
334 shared_ptr<Undo> undo = stack.top();
340 Buffer * buf = bv->buffer();
341 ParIterator p = b->getParFromID(undo->number_of_before_par);
342 ParIterator const end = b->par_iterator_end();
344 ParagraphList::iterator first;
345 // default constructed?
349 first = first->next();
351 first = undoParagraphs(bv, undo->number_of_inset_id)->begin();
354 ParIterator behind = b->getParFromID(undo->number_of_behind_par);
355 if (createUndo(bv, undo->kind, first, behind.par(), u))
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
366 bv->unlockInset(bv->theLockingInset());
367 bool const ret = textHandleUndo(bv, *undo.get());
377 // Makes sure the next operation will be stored.
378 undo_finished = true;
384 // This is dangerous and for internal use only.
391 // This is dangerous and for internal use only.
396 bool textUndo(BufferView * bv)
398 return textUndoOrRedo(bv, bv->buffer()->undostack,
399 bv->buffer()->redostack);
403 bool textRedo(BufferView * bv)
405 return textUndoOrRedo(bv, bv->buffer()->redostack,
406 bv->buffer()->undostack);
410 void setUndo(BufferView * bv, Undo::undo_kind kind,
411 ParagraphList::iterator first)
413 setUndo(bv, kind, first, first);
417 void setUndo(BufferView * bv, Undo::undo_kind kind,
418 ParagraphList::iterator first, ParagraphList::iterator last)
424 if (createUndo(bv, kind, first->id(), last->id(), u))
425 bv->buffer()->undostack.push(u);
426 bv->buffer()->redostack.clear();
431 void setCursorParUndo(BufferView * bv)
433 setUndo(bv, Undo::FINISH, bv->text->cursor.par());