X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fundo_funcs.C;h=8d15fed249e471e83c8f8e7406f3aefab029e192;hb=6ce86e2bfe0a403e0e811b66fdddb2d56cfe0f83;hp=d1b0f2ab48425083386cc9f37cbdc96c243e8e24;hpb=7521b5d20f42102cf444e3fd8718a088a60d0098;p=lyx.git diff --git a/src/undo_funcs.C b/src/undo_funcs.C index d1b0f2ab48..8d15fed249 100644 --- a/src/undo_funcs.C +++ b/src/undo_funcs.C @@ -1,233 +1,278 @@ /* This file is part of - * ====================================================== - * + * ====================================================== + * * LyX, The Document Processor - * + * * Copyright 1995-2001 The LyX Team. * * ====================================================== */ #include -#ifdef __GNUG__ -#pragma implementation -#endif - #include "undo_funcs.h" #include "lyxtext.h" #include "BufferView.h" #include "buffer.h" -#include "insets/inset.h" +#include "insets/updatableinset.h" #include "debug.h" +#include "support/LAssert.h" + +#include "iterators.h" + +#include + +using std::vector; +using boost::shared_ptr; + /// the flag used by FinishUndo(); bool undo_finished; -/// a flag +/// whether actions are not added to the undo stacks bool undo_frozen; -bool textUndo(BufferView * bv) +namespace { + +/// utility to return the cursor +LyXCursor const & undoCursor(BufferView * bv) { - // returns false if no undo possible - Undo * undo = bv->buffer()->undostack.pop(); - if (undo) { - finishUndo(); - if (!undo_frozen) { - Paragraph * first = bv->text->getParFromID(undo->number_of_before_par); - if (!first) - first = firstUndoParagraph(bv, undo->number_of_inset_id); - if (first) { - bv->buffer()->redostack.push( - createUndo(bv, undo->kind, first, - bv->text->getParFromID(undo->number_of_behind_par))); - } - } + if (bv->theLockingInset()) + return bv->theLockingInset()->cursor(bv); + return bv->text->cursor; +} + +/** + * returns a pointer to the very first Paragraph depending of where we are + * so it will return the first paragraph of the buffer or the first paragraph + * of the textinset we're in. + */ +Paragraph * firstUndoParagraph(BufferView * bv, int inset_id) +{ + Inset * inset = bv->buffer()->getInsetFromID(inset_id); + if (inset) { + Paragraph * result = inset->getFirstParagraph(0); + if (result) + return result; } - return textHandleUndo(bv, undo); + return &*bv->text->ownerParagraphs().begin(); } -bool textRedo(BufferView * bv) +/** + * Finish the undo operation in the case there was no entry + * on the stack to perform. + */ +void finishNoUndo(BufferView * bv) { - // returns false if no redo possible - Undo * undo = bv->buffer()->redostack.pop(); - if (undo) { - finishUndo(); - if (!undo_frozen) { - Paragraph * first = bv->text->getParFromID(undo->number_of_before_par); - if (!first) - first = firstUndoParagraph(bv, undo->number_of_inset_id); - if (first) { - bv->buffer()->undostack.push( - createUndo(bv, undo->kind, first, - bv->text->getParFromID(undo->number_of_behind_par))); - } - } - } - return textHandleUndo(bv, undo); + freezeUndo(); + bv->unlockInset(bv->theLockingInset()); + finishUndo(); + bv->text->postPaint(0); + unFreezeUndo(); } -bool textHandleUndo(BufferView * bv, Undo * undo) +// returns false if no undo possible +bool textHandleUndo(BufferView * bv, Undo & undo) { - // returns false if no undo possible - bool result = false; - if (undo) { - Paragraph * before = - bv->text->getParFromID(undo->number_of_before_par); - Paragraph * behind = - bv->text->getParFromID(undo->number_of_behind_par); - Paragraph * tmppar; - Paragraph * tmppar2; - Paragraph * endpar; - 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; - } + 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; + + // 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); } + t->setCursorIntern(firstUndoParagraph(bv, num), 0); + } - // replace the paragraphs with the undo informations - - Paragraph * tmppar3 = undo->par; - undo->par = 0; // otherwise the undo destructor would delete the paragraph - Paragraph * tmppar4 = tmppar3; - - if (tmppar4) { - while (tmppar4->next()) - tmppar4 = tmppar4->next(); - } // get last undo par - - // now remove the old text if there is any - 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) { - 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); - tmppar->clearContents(); - 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(tmppar3->id(), tmppar3); - tmppar3->previous(before); - } else { - // Do we really enter here ??? (Jug) - if (!before) - bv->text->ownerParagraph(behind); - } - if (tmppar4) { - tmppar4->next(behind); - if (behind) - behind->previous(tmppar4); - } - - - // Set the cursor for redoing - if (before) { - bv->text->setCursorIntern(bv, before, 0); + // replace the paragraphs with the undo informations + + Paragraph * undopar = 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 * lastundopar = undopar; + if (lastundopar) { + 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); + lastundopar->setInsetOwner(in); + while (lastundopar->next()) { + lastundopar = lastundopar->next(); + lastundopar->setInsetOwner(in); } + } - // calculate the endpar for redoing the paragraphs. - if (behind) { - endpar = behind->next(); - } else - endpar = behind; - - tmppar = bv->text->getParFromID(undo->number_of_cursor_par); - UpdatableInset* it = static_cast(tmppar3->InInset()); - if (it) { - it->getLyXText(bv)->redoParagraphs(bv, it->getLyXText(bv)->cursor, - endpar); - if (tmppar){ - it->getLyXText(bv)->setCursorIntern(bv, tmppar, undo->cursor_pos); - it->getLyXText(bv)->updateCounters(bv, it->getLyXText(bv)->cursor.row()); - } -#ifdef THIS_DOES_NOT_WORK - // we need this anyway as also if the undo was inside an inset - // we have to redo the paragraph breaking - bv->text->redoParagraphs(bv, bv->text->cursor, - bv->text->cursor.par()); -#endif - } else { - bv->text->redoParagraphs(bv, bv->text->cursor, endpar); - if (tmppar) { - bv->text->setCursorIntern(bv, tmppar, undo->cursor_pos); - bv->text->updateCounters(bv, bv->text->cursor.row()); + vector deletelist; + + Paragraph * deletepar; + + // now add old paragraphs to be deleted + if (before != behind || (!behind && !before)) { + if (before) + deletepar = before->next(); + else + deletepar = firstUndoParagraph(bv, undo.number_of_inset_id); + tmppar2 = undopar; + while (deletepar && deletepar != behind) { + deletelist.push_back(deletepar); + tmppar = deletepar; + deletepar = deletepar->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(); } } - result = true; - delete undo; } - finishUndo(); - bv->text->status(bv, LyXText::NEED_MORE_REFRESH); - return result; -} - -void finishUndo() -{ - // makes sure the next operation will be stored - undo_finished = true; -} + // The order here is VERY IMPORTANT. We have to set the right + // next/prev pointer in the paragraphs so that a rebuild of + // the LyXText works!!! + // thread the end of the undo onto the par in front if any + if (lastundopar) { + lastundopar->next(behind); + if (behind) + behind->previous(lastundopar); + } -void freezeUndo() -{ - // this is dangerous and for internal use only - undo_frozen = true; -} + // put the new stuff in the list if there is one + if (undopar) { + undopar->previous(before); + if (before) + before->next(undopar); + else + bv->text->ownerParagraph(firstUndoParagraph(bv, undo.number_of_inset_id)->id(), + undopar); + + } 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); + undopar = behind; + } + } -void unFreezeUndo() -{ - // this is dangerous and for internal use only - undo_frozen = false; -} - + // Set the cursor for redoing + if (before) { // if we have a par before the undopar + Inset * it = before->inInset(); + if (it) + it->getLyXText(bv)->setCursorIntern(before, 0); + else + bv->text->setCursorIntern(before, 0); + } +// we are not ready for this we cannot set the cursor for a paragraph +// which is not already in a row of LyXText!!! +#if 0 + else { // otherwise this is the first one and we start here + Inset * it = undopar->inInset(); + if (it) + it->getLyXText(bv)->setCursorIntern(bv, undopar, 0); + else + bv->text->setCursorIntern(bv, undopar, 0); + } +#endif -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(); -} + 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 (undopar) + it = static_cast(undopar->inInset()); + if (it) { + it->getLyXText(bv)->redoParagraphs( + it->getLyXText(bv)->cursor, + endpar); + if (tmppar) { + it = static_cast(tmppar->inInset()); + LyXText * t; + if (it) { + it->edit(bv); + t = it->getLyXText(bv); + } else { + t = bv->text; + } + t->setCursorIntern(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->fitCursor(); + } + bv->updateInset(it); + bv->text->setCursorIntern(bv->text->cursor.par(), + bv->text->cursor.pos()); + } else { + bv->text->redoParagraphs(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(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(); + } + } + // And here it's safe enough to delete all removed paragraphs + vector::iterator pit = deletelist.begin(); + if (pit != deletelist.end()) { + for(;pit != deletelist.end(); ++pit) { + (*pit)->previous(0); + (*pit)->next(0); + delete (*pit); + } + } -void setRedo(BufferView * bv, Undo::undo_kind kind, - Paragraph const * first, Paragraph const * behind) -{ - bv->buffer()->redostack.push(createUndo(bv, kind, first, behind)); + finishUndo(); + bv->text->postPaint(0); + 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 & u) { lyx::Assert(first); @@ -239,38 +284,35 @@ Undo * createUndo(BufferView * bv, Undo::undo_kind kind, before_number = first->previous()->id(); if (behind) behind_number = behind->id(); - if (first->InInset()) - inset_id = first->InInset()->id(); + 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: + // overlapping only with insert and delete inside one paragraph: // Nobody wants all removed character - // appear one by one when undoing. + // appear one by one when undoing. // 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 ){ + if (!undo_finished && (kind != Undo::EDIT) && (kind != Undo::FINISH)) { + // 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; Paragraph * start = const_cast(first); Paragraph * end = 0; -#if 0 - if (first) - start = const_cast(before->next()); - else - start = firstUndoParagraph(bv); -#endif if (behind) end = const_cast(behind->previous()); else { @@ -280,11 +322,11 @@ Undo * createUndo(BufferView * bv, Undo::undo_kind kind, } if (start && end && (start != end->next()) && ((before_number != behind_number) || - ((before_number < 0) && (behind_number < 0)))) { + ((before_number < 0) && (behind_number < 0)))) + { Paragraph * tmppar = start; Paragraph * tmppar2 = new Paragraph(*tmppar, true); - tmppar2->id(tmppar->id()); - + // a memory optimization: Just store the layout information // when only edit if (kind == Undo::EDIT) { @@ -292,17 +334,17 @@ Undo * createUndo(BufferView * bv, Undo::undo_kind kind, } undopar = tmppar2; - + while (tmppar != end && tmppar->next()) { tmppar = tmppar->next(); - tmppar2->next(new Paragraph(*tmppar)); - tmppar2->next()->id(tmppar->id()); + tmppar2->next(new Paragraph(*tmppar, true)); // a memory optimization: Just store the layout // information when only edit if (kind == Undo::EDIT) { tmppar2->clearContents(); } tmppar2->next()->previous(tmppar2); + tmppar2 = tmppar2->next(); } tmppar2->next(0); @@ -311,37 +353,139 @@ Undo * createUndo(BufferView * bv, Undo::undo_kind kind, 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 setCursorParUndo(BufferView * bv) +void finishUndo() { - setUndo(bv, Undo::FINISH, - bv->text->cursor.par(), - bv->text->cursor.par()->next()); + // makes sure the next operation will be stored + undo_finished = true; } -Paragraph * firstUndoParagraph(BufferView * bv, int inset_id) + +void freezeUndo() { - Inset * inset = bv->buffer()->getInsetFromID(inset_id); - if (inset) { - Paragraph * result = inset->firstParagraph(); - if (result) - return result; + // 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 = 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 u; + if (createUndo(bv, undo->kind, first, + b->getParFromID(undo->number_of_behind_par), u)) + b->redostack.push(u); + } } - return bv->text->ownerParagraph(); + + // 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; } -LyXCursor const & undoCursor(BufferView * bv) + +// returns false if no redo possible +bool textRedo(BufferView * bv) { - if (bv->theLockingInset()) - return bv->theLockingInset()->cursor(bv); - return bv->text->cursor; + Buffer * b = bv->buffer(); + + if (b->redostack.empty()) { + finishNoUndo(bv); + return false; + } + + shared_ptr 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 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 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 u; + if (createUndo(bv, kind, first, behind, u)) + bv->buffer()->redostack.push(u); +} + + +void setCursorParUndo(BufferView * bv) +{ + setUndo(bv, Undo::FINISH, bv->text->cursor.par(), + bv->text->cursor.par()->next()); }