OK this kills the Undo * thing in favour of boost::shared_ptr. It's valgrind clean, and seems to work fine for me. Getting undo_funcs.C into readable shape is a job for another day.
I've not included boring parts of the patch like removal of undostack files OK to apply ? thanks john -- "Time is a great teacher, but unfortunately it kills all its pupils." - Hector Louis Berlioz
Index: src/undo_funcs.h =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/undo_funcs.h,v retrieving revision 1.2 diff -u -r1.2 undo_funcs.h --- src/undo_funcs.h 21 Mar 2002 17:25:32 -0000 1.2 +++ src/undo_funcs.h 26 May 2002 18:58:05 -0000 @@ -25,8 +25,6 @@ extern bool textUndo(BufferView *); /// returns false if no redo possible extern bool textRedo(BufferView *); -/// used by TextUndo/TextRedo -extern bool textHandleUndo(BufferView *, Undo * undo); /// makes sure the next operation will be stored extern void finishUndo(); /// this is dangerous and for internal use only @@ -40,9 +38,6 @@ extern void setRedo(BufferView *, Undo::undo_kind kind, Paragraph const * first, Paragraph const * behind); /// -extern Undo * createUndo(BufferView *, Undo::undo_kind kind, - Paragraph const * first, Paragraph const * behind); -/// for external use in lyx_cb.C extern void setCursorParUndo(BufferView *); // returns a pointer to the very first Paragraph depending of where we are @@ -53,8 +48,6 @@ /// extern LyXCursor const & undoCursor(BufferView * bv); -/// the flag used by FinishUndo(); -extern bool undo_finished; /// a flag extern bool undo_frozen; Index: src/undo_funcs.C =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/undo_funcs.C,v retrieving revision 1.23 diff -u -r1.23 undo_funcs.C --- src/undo_funcs.C 24 May 2002 10:37:46 -0000 1.23 +++ src/undo_funcs.C 26 May 2002 18:58:05 -0000 @@ -23,328 +23,212 @@ #include "iterators.h" -#define DELETE_UNUSED_PARAGRAPHS 1 -#ifdef DELETE_UNUSED_PARAGRAPHS #include <vector> -#endif using std::vector; +using boost::shared_ptr; /// the flag used by FinishUndo(); bool undo_finished; -/// a flag +/// FIXME bool undo_frozen; +namespace { -bool textUndo(BufferView * bv) +void finishNoUndo(BufferView * bv) { - // returns false if no undo possible - Undo * undo = bv->buffer()->undostack.top(); - bv->buffer()->undostack.pop(); - if (undo) { - finishUndo(); - if (!undo_frozen) { - Paragraph * first = bv->buffer()->getParFromID(undo->number_of_before_par); - if (first && first->next()) - first = first->next(); - else if (!first) - first = firstUndoParagraph(bv, undo->number_of_inset_id); - if (first) { - bv->buffer()->redostack.push( - createUndo(bv, undo->kind, first, - bv->buffer()->getParFromID(undo->number_of_behind_par))); - } - } - } - // now we can unlock the inset for saftey because the inset pointer could - // be changed during the undo-function. Anyway if needed we have to lock - // the right inset/position if this is requested. freezeUndo(); bv->unlockInset(bv->theLockingInset()); - bool ret = textHandleUndo(bv, undo); + finishUndo(); + bv->text->status(bv, LyXText::NEED_MORE_REFRESH); unFreezeUndo(); - return ret; } - - -bool textRedo(BufferView * bv) + + +// returns false if no undo possible +bool textHandleUndo(BufferView * bv, Undo & undo) { - // returns false if no redo possible - Undo * undo = bv->buffer()->redostack.top(); - bv->buffer()->redostack.pop(); - if (undo) { - finishUndo(); - if (!undo_frozen) { - Paragraph * first = bv->buffer()->getParFromID(undo->number_of_before_par); - if (first && first->next()) - first = first->next(); - else if (!first) - first = firstUndoParagraph(bv, undo->number_of_inset_id); - if (first) { - bv->buffer()->undostack.push( - createUndo(bv, undo->kind, first, - bv->buffer()->getParFromID(undo->number_of_behind_par))); + Buffer * b = bv->buffer(); + + Paragraph * before = + b->getParFromID(undo.number_of_before_par); + Paragraph * behind = + b->getParFromID(undo.number_of_behind_par); + Paragraph * tmppar; + Paragraph * tmppar2; + Paragraph * tmppar5; + + // if there's no before take the beginning + // of the document for redoing + if (!before) { + LyXText * t = bv->text; + int num = undo.number_of_inset_id; + if (undo.number_of_inset_id >= 0) { + Inset * in = bv->buffer()->getInsetFromID(num); + if (in) { + t = in->getLyXText(bv); + } else { + num = -1; } } + t->setCursorIntern(bv, firstUndoParagraph(bv, num), 0); } - // now we can unlock the inset for saftey because the inset pointer could - // be changed during the undo-function. Anyway if needed we have to lock - // the right inset/position if this is requested. - freezeUndo(); - bv->unlockInset(bv->theLockingInset()); - bool ret = textHandleUndo(bv, undo); - unFreezeUndo(); - return ret; -} - - -bool textHandleUndo(BufferView * bv, Undo * undo) -{ - // returns false if no undo possible - bool result = false; - if (undo) { - Paragraph * before = - bv->buffer()->getParFromID(undo->number_of_before_par); - Paragraph * behind = - bv->buffer()->getParFromID(undo->number_of_behind_par); - Paragraph * tmppar; - Paragraph * tmppar2; - Paragraph * tmppar5; - - // if there's no before take the beginning - // of the document for redoing - if (!before) { - LyXText * t = bv->text; - int num = undo->number_of_inset_id; - if (undo->number_of_inset_id >= 0) { - Inset * in = bv->buffer()->getInsetFromID(num); - if (in) { - t = in->getLyXText(bv); - } else { - num = -1; - } - } - t->setCursorIntern(bv, firstUndoParagraph(bv, num), 0); - } - // replace the paragraphs with the undo informations + // replace the paragraphs with the undo informations - Paragraph * tmppar3 = undo->par; - undo->par = 0; /* otherwise the undo destructor would - delete the paragraph */ - - // get last undo par and set the right(new) inset-owner of the - // paragraph if there is any. This is not needed if we don't have - // a paragraph before because then in is automatically done in the - // function which assigns the first paragraph to an InsetText. (Jug) - Paragraph * tmppar4 = tmppar3; - if (tmppar4) { - Inset * in = 0; - if (before) - in = before->inInset(); - else if (undo->number_of_inset_id >= 0) - in = bv->buffer()->getInsetFromID(undo->number_of_inset_id); + Paragraph * tmppar3 = undo.par; + undo.par = 0; /* otherwise the undo destructor would + delete the paragraph */ + + // get last undo par and set the right(new) inset-owner of the + // paragraph if there is any. This is not needed if we don't have + // a paragraph before because then in is automatically done in the + // function which assigns the first paragraph to an InsetText. (Jug) + Paragraph * tmppar4 = tmppar3; + if (tmppar4) { + Inset * in = 0; + if (before) + in = before->inInset(); + else if (undo.number_of_inset_id >= 0) + in = bv->buffer()->getInsetFromID(undo.number_of_inset_id); + tmppar4->setInsetOwner(in); + while (tmppar4->next()) { + tmppar4 = tmppar4->next(); tmppar4->setInsetOwner(in); - while (tmppar4->next()) { - tmppar4 = tmppar4->next(); - tmppar4->setInsetOwner(in); - } } + } - // now remove the old text if there is any -#ifdef DELETE_UNUSED_PARAGRAPHS - vector<Paragraph *> vvpar; -#endif - if (before != behind || (!behind && !before)) { - if (before) - tmppar5 = before->next(); - else - tmppar5 = firstUndoParagraph(bv, undo->number_of_inset_id); - tmppar2 = tmppar3; - while (tmppar5 && tmppar5 != behind) { -#ifdef DELETE_UNUSED_PARAGRAPHS - vvpar.push_back(tmppar5); -#endif - tmppar = tmppar5; - tmppar5 = tmppar5->next(); - // a memory optimization for edit: - // Only layout information - // is stored in the undo. So restore - // the text informations. - if (undo->kind == Undo::EDIT) { - tmppar2->setContentsFromPar(tmppar); -#ifndef DELETE_UNUSED_PARAGRAPHS - tmppar->clearContents(); -#endif - tmppar2 = tmppar2->next(); - } + // now remove the old text if there is any + vector<Paragraph *> vvpar; + if (before != behind || (!behind && !before)) { + if (before) + tmppar5 = before->next(); + else + tmppar5 = firstUndoParagraph(bv, undo.number_of_inset_id); + tmppar2 = tmppar3; + while (tmppar5 && tmppar5 != behind) { + vvpar.push_back(tmppar5); + tmppar = tmppar5; + tmppar5 = tmppar5->next(); + + // a memory optimization for edit: + // Only layout information + // is stored in the undo. So restore + // the text informations. + if (undo.kind == Undo::EDIT) { + tmppar2->setContentsFromPar(tmppar); + tmppar2 = tmppar2->next(); } } + } - // put the new stuff in the list if there is one - if (tmppar3) { - if (before) - before->next(tmppar3); - else - bv->text->ownerParagraph(firstUndoParagraph(bv, undo->number_of_inset_id)->id(), - tmppar3); - - tmppar3->previous(before); - } else { - // We enter here on DELETE undo operations where we have to - // substitue the second paragraph with the first if the removed - // one is the first! - if (!before && behind) { - bv->text->ownerParagraph(firstUndoParagraph(bv, undo->number_of_inset_id)->id(), - behind); - tmppar3 = behind; - } - } - if (tmppar4) { - tmppar4->next(behind); - if (behind) - behind->previous(tmppar4); + // put the new stuff in the list if there is one + if (tmppar3) { + if (before) + before->next(tmppar3); + else + bv->text->ownerParagraph(firstUndoParagraph(bv, +undo.number_of_inset_id)->id(), + tmppar3); + + tmppar3->previous(before); + } else { + // We enter here on DELETE undo operations where we have to + // substitue the second paragraph with the first if the removed + // one is the first! + if (!before && behind) { + bv->text->ownerParagraph(firstUndoParagraph(bv, +undo.number_of_inset_id)->id(), + behind); + tmppar3 = behind; } + } + if (tmppar4) { + tmppar4->next(behind); + if (behind) + behind->previous(tmppar4); + } - // Set the cursor for redoing - if (before) { - Inset * it = before->inInset(); - if (it) - it->getLyXText(bv)->setCursorIntern(bv, before, 0); - else - bv->text->setCursorIntern(bv, before, 0); - } + // Set the cursor for redoing + if (before) { + Inset * it = before->inInset(); + if (it) + it->getLyXText(bv)->setCursorIntern(bv, before, 0); + else + bv->text->setCursorIntern(bv, before, 0); + } - Paragraph * endpar = 0; - // calculate the endpar for redoing the paragraphs. - if (behind) - endpar = behind->next(); + Paragraph * endpar = 0; + // calculate the endpar for redoing the paragraphs. + if (behind) + endpar = behind->next(); - tmppar = bv->buffer()->getParFromID(undo->number_of_cursor_par); - UpdatableInset* it = 0; - if (tmppar3) - it = static_cast<UpdatableInset*>(tmppar3->inInset()); - if (it) { - it->getLyXText(bv)->redoParagraphs(bv, - it->getLyXText(bv)->cursor, - endpar); - if (tmppar) { - it = static_cast<UpdatableInset*>(tmppar->inInset()); - LyXText * t; - if (it) { - it->edit(bv); - t = it->getLyXText(bv); - } else { - t = bv->text; - } - t->setCursorIntern(bv, tmppar, undo->cursor_pos); - // clear any selection and set the selection cursor - // for an evt. new selection. - t->clearSelection(); - t->selection.cursor = t->cursor; - t->updateCounters(bv, t->cursor.row()); - bv->fitCursor(); + tmppar = bv->buffer()->getParFromID(undo.number_of_cursor_par); + UpdatableInset* it = 0; + if (tmppar3) + it = static_cast<UpdatableInset*>(tmppar3->inInset()); + if (it) { + it->getLyXText(bv)->redoParagraphs(bv, + it->getLyXText(bv)->cursor, + endpar); + if (tmppar) { + it = static_cast<UpdatableInset*>(tmppar->inInset()); + LyXText * t; + if (it) { + it->edit(bv); + t = it->getLyXText(bv); + } else { + t = bv->text; } - bv->updateInset(it, false); - bv->text->setCursorIntern(bv, bv->text->cursor.par(), - bv->text->cursor.pos()); - } else { - bv->text->redoParagraphs(bv, bv->text->cursor, endpar); - if (tmppar) { - LyXText * t; - Inset * it = tmppar->inInset(); - if (it) { - it->edit(bv); - t = it->getLyXText(bv); - } else { - t = bv->text; - } - t->setCursorIntern(bv, tmppar, undo->cursor_pos); - // clear any selection and set the selection cursor - // for an evt. new selection. - t->clearSelection(); - t->selection.cursor = t->cursor; - t->updateCounters(bv, t->cursor.row()); + t->setCursorIntern(bv, tmppar, undo.cursor_pos); + // clear any selection and set the selection cursor + // for an evt. new selection. + t->clearSelection(); + t->selection.cursor = t->cursor; + t->updateCounters(bv, t->cursor.row()); + bv->fitCursor(); + } + bv->updateInset(it, false); + bv->text->setCursorIntern(bv, bv->text->cursor.par(), + bv->text->cursor.pos()); + } else { + bv->text->redoParagraphs(bv, bv->text->cursor, endpar); + if (tmppar) { + LyXText * t; + Inset * it = tmppar->inInset(); + if (it) { + it->edit(bv); + t = it->getLyXText(bv); + } else { + t = bv->text; } + t->setCursorIntern(bv, tmppar, undo.cursor_pos); + // clear any selection and set the selection cursor + // for an evt. new selection. + t->clearSelection(); + t->selection.cursor = t->cursor; + t->updateCounters(bv, t->cursor.row()); } - result = true; - delete undo; -#ifdef DELETE_UNUSED_PARAGRAPHS - // And here it's save enough to delete all removed paragraphs - vector<Paragraph *>::iterator pit = vvpar.begin(); - if (pit != vvpar.end()) { -#if 0 - lyxerr << endl << "PARS BEFORE:"; - ParIterator end = bv->buffer()->par_iterator_end(); - ParIterator it = bv->buffer()->par_iterator_begin(); - for (; it != end; ++it) - lyxerr << (*it)->previous() << "<- " << (*it) << " ->" << (*it)->next() << endl; - lyxerr << "DEL: "; -#endif + } + + // And here it's save enough to delete all removed paragraphs + vector<Paragraph *>::iterator pit = vvpar.begin(); + if (pit != vvpar.end()) { for(;pit != vvpar.end(); ++pit) { -// lyxerr << *pit << " "; (*pit)->previous(0); (*pit)->next(0); delete (*pit); } -#if 0 - lyxerr << endl << "PARS AFTER:"; - end = bv->buffer()->par_iterator_end(); - it = bv->buffer()->par_iterator_begin(); - for (; it != end; ++it) - lyxerr << (*it)->previous() << "<- " << (*it) << " ->" << (*it)->next() << endl; -#endif } -#endif - } + finishUndo(); bv->text->status(bv, LyXText::NEED_MORE_REFRESH); - return result; -} - - -void finishUndo() -{ - // makes sure the next operation will be stored - undo_finished = true; -} - - -void freezeUndo() -{ - // this is dangerous and for internal use only - undo_frozen = true; -} - - -void unFreezeUndo() -{ - // this is dangerous and for internal use only - undo_frozen = false; -} - - -void setUndo(BufferView * bv, Undo::undo_kind kind, - Paragraph const * first, Paragraph const * behind) -{ - if (!undo_frozen) { - bv->buffer()->undostack.push(createUndo(bv, kind, first, behind)); - bv->buffer()->redostack.clear(); - } -} - - -void setRedo(BufferView * bv, Undo::undo_kind kind, - Paragraph const * first, Paragraph const * behind) -{ - bv->buffer()->redostack.push(createUndo(bv, kind, first, behind)); + return true; } - -Undo * createUndo(BufferView * bv, Undo::undo_kind kind, - Paragraph const * first, Paragraph const * behind) + +bool createUndo(BufferView * bv, Undo::undo_kind kind, + Paragraph const * first, Paragraph const * behind, shared_ptr<Undo> & u) { lyx::Assert(first); @@ -359,6 +243,8 @@ if (first->inInset()) inset_id = first->inInset()->id(); + Buffer * b = bv->buffer(); + // Undo::EDIT and Undo::FINISH are // always finished. (no overlapping there) // overlapping only with insert and delete inside one paragraph: @@ -367,15 +253,16 @@ // EDIT is special since only layout information, not the // contents of a paragaph are stored. if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) { - // check wether storing is needed - if (!bv->buffer()->undostack.empty() && - bv->buffer()->undostack.top()->kind == kind && - bv->buffer()->undostack.top()->number_of_before_par == before_number && - bv->buffer()->undostack.top()->number_of_behind_par == behind_number) { + // check whether storing is needed + if (!b->undostack.empty() && + b->undostack.top()->kind == kind && + b->undostack.top()->number_of_before_par == before_number && + b->undostack.top()->number_of_behind_par == behind_number) { // no undo needed - return 0; + return false; } } + // create a new Undo Paragraph * undopar; @@ -428,12 +315,133 @@ int cursor_par = undoCursor(bv).par()->id(); int cursor_pos = undoCursor(bv).pos(); - Undo * undo = new Undo(kind, inset_id, - before_number, behind_number, - cursor_par, cursor_pos, undopar); + u.reset(new Undo(kind, inset_id, + before_number, behind_number, + cursor_par, cursor_pos, undopar)); undo_finished = false; - return undo; + return true; +} + +} // namespace anon + +void finishUndo() +{ + // makes sure the next operation will be stored + undo_finished = true; +} + + +void freezeUndo() +{ + // this is dangerous and for internal use only + undo_frozen = true; +} + + +void unFreezeUndo() +{ + // this is dangerous and for internal use only + undo_frozen = false; +} + + +// returns false if no undo possible +bool textUndo(BufferView * bv) +{ + Buffer * b = bv->buffer(); + + if (b->undostack.empty()) { + finishNoUndo(bv); + return false; + } + + shared_ptr<Undo> undo = b->undostack.top(); + b->undostack.pop(); + finishUndo(); + + if (!undo_frozen) { + Paragraph * first = b->getParFromID(undo->number_of_before_par); + if (first && first->next()) + first = first->next(); + else if (!first) + first = firstUndoParagraph(bv, undo->number_of_inset_id); + if (first) { + shared_ptr<Undo> u; + if (createUndo(bv, undo->kind, first, + b->getParFromID(undo->number_of_behind_par), u)) + b->redostack.push(u); + } + } + + // now we can unlock the inset for saftey because the inset pointer could + // be changed during the undo-function. Anyway if needed we have to lock + // the right inset/position if this is requested. + freezeUndo(); + bv->unlockInset(bv->theLockingInset()); + bool const ret = textHandleUndo(bv, *undo.get()); + unFreezeUndo(); + return ret; +} + + +// returns false if no redo possible +bool textRedo(BufferView * bv) +{ + Buffer * b = bv->buffer(); + + if (b->redostack.empty()) { + finishNoUndo(bv); + return false; + } + + shared_ptr<Undo> undo = b->redostack.top(); + b->redostack.pop(); + finishUndo(); + + if (!undo_frozen) { + Paragraph * first = bv->buffer()->getParFromID(undo->number_of_before_par); + if (first && first->next()) + first = first->next(); + else if (!first) + first = firstUndoParagraph(bv, undo->number_of_inset_id); + if (first) { + shared_ptr<Undo> u; + if (createUndo(bv, undo->kind, first, + +bv->buffer()->getParFromID(undo->number_of_behind_par), u)) + bv->buffer()->undostack.push(u); + } + } + + // now we can unlock the inset for saftey because the inset pointer could + // be changed during the undo-function. Anyway if needed we have to lock + // the right inset/position if this is requested. + freezeUndo(); + bv->unlockInset(bv->theLockingInset()); + bool ret = textHandleUndo(bv, *undo.get()); + unFreezeUndo(); + return ret; +} + + +void setUndo(BufferView * bv, Undo::undo_kind kind, + Paragraph const * first, Paragraph const * behind) +{ + if (!undo_frozen) { + shared_ptr<Undo> u; + if (createUndo(bv, kind, first, behind, u)) + bv->buffer()->undostack.push(u); + bv->buffer()->redostack.clear(); + } +} + + +void setRedo(BufferView * bv, Undo::undo_kind kind, + Paragraph const * first, Paragraph const * behind) +{ + shared_ptr<Undo> u; + if (createUndo(bv, kind, first, behind, u)) + bv->buffer()->redostack.push(u); } Index: src/buffer.h =================================================================== RCS file: /usr/local/lyx/cvsroot/lyx-devel/src/buffer.h,v retrieving revision 1.97 diff -u -r1.97 buffer.h --- src/buffer.h 22 May 2002 12:33:02 -0000 1.97 +++ src/buffer.h 26 May 2002 18:58:05 -0000 @@ -19,12 +19,15 @@ #include "LString.h" #include "undo.h" -#include "undostack.h" +#include "support/limited_stack.h" + #include "lyxvc.h" #include "bufferparams.h" #include "texrow.h" #include "paragraph.h" +#include <boost/shared_ptr.hpp> + class BufferView; class LyXRC; class TeXErrors; @@ -298,11 +301,11 @@ bool isMultiLingual(); /// Does this mean that this is buffer local? - UndoStack undostack; - - /// Does this mean that this is buffer local? - UndoStack redostack; - + limited_stack<boost::shared_ptr<Undo> > undostack; + + /// Does this mean that this is buffer local? + limited_stack<boost::shared_ptr<Undo> > redostack; + /// BufferParams params; Index: src/support/limited_stack.h =================================================================== RCS file: src/support/limited_stack.h diff -N src/support/limited_stack.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/support/limited_stack.h 26 May 2002 18:58:05 -0000 @@ -0,0 +1,77 @@ +// -*- C++ -*- +/** + * \file limited_stack.h + * Copyright 2002 the LyX Team + * Read the file COPYING + * + * \author John Levon <[EMAIL PROTECTED]> + */ + +#ifndef LIMITED_STACK_H +#define LIMITED_STACK_H + +#include <list> + +#include <boost/shared_ptr.hpp> + +/** + * limited_stack - a stack of limited size + * + * Like a normal stack, but only accepts pointer types, + * and bottom elements are deleted on overflow + */ +template <typename T> +class limited_stack { +public: + typedef std::list<T> container_type; + typedef typename container_type::value_type value_type; + typedef typename container_type::size_type size_type; + + /// limit is the maximum size of the stack + limited_stack(size_type limit = 10) { + limit_ = limit; + } + + /// return the top element + value_type top() { + return c_.front(); + } + + /// pop and throw away the top element + void pop() { + c_.pop_front(); + } + + /// return true if the stack is empty + bool empty() const { + return c_.size() == 0; + } + + /// clear all elements, deleting them + void clear() { + while (!c_.empty()) { + c_.pop_back(); + } + } + + /// push an item on to the stack, deleting the + /// bottom item on overflow. + void push(value_type const & v) { + c_.push_front(v); + if (c_.size() > limit_) { + c_.pop_back(); + } + } + +private: + /// internal contents + container_type c_; + /// the maximum number elements stored + size_type limit_; +}; + +// make pointer type an error. +template <typename T> +class limited_stack<T*>; + +#endif // LIMITED_STACK_H