From 983d23f87d4f6d908b4f79c237ce97434a23d2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20P=C3=B6nitz?= Date: Fri, 23 May 2003 10:33:40 +0000 Subject: [PATCH] setUndo2 interface change access to topmost plist item (as reference...) git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@7022 a592a061-630c-0410-9148-cb99ea01b6c8 --- src/BufferView.C | 4 +- src/ChangeLog | 10 +++ src/buffer.C | 4 +- src/insets/insettabular.C | 16 +--- src/insets/insettext.C | 5 +- src/iterators.C | 6 ++ src/iterators.h | 2 + src/text.C | 19 ++--- src/text2.C | 24 +++--- src/text3.C | 4 +- src/text_funcs.C | 2 +- src/undo_funcs.C | 150 +++++++++++++++++++------------------- src/undo_funcs.h | 21 +++--- 13 files changed, 136 insertions(+), 131 deletions(-) diff --git a/src/BufferView.C b/src/BufferView.C index 7bd571ed9a..11a0a477c0 100644 --- a/src/BufferView.C +++ b/src/BufferView.C @@ -604,9 +604,7 @@ void BufferView::lockedInsetStoreUndo(Undo::undo_kind kind) return; // shouldn't happen if (kind == Undo::EDIT) // in this case insets would not be stored! kind = Undo::FINISH; - setUndo(this, kind, - text->cursor.par(), - boost::next(text->cursor.par())); + setUndo(this, kind, text->cursor.par()); } diff --git a/src/ChangeLog b/src/ChangeLog index ad06f36078..6ff99f88b2 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2003-05-23 André Pönitz + + * BufferView.C: + * BufferView_pimpl.C: + * buffer.C: + * buffer.h: + * lyxfunc.C: + * undo_funcs.C: setUndo reworked + + * iterators.[Ch]: add access to topmost ParagraphList 2003-05-23 Alfredo Braunstein diff --git a/src/buffer.C b/src/buffer.C index 4029eb4000..22314c3f9a 100644 --- a/src/buffer.C +++ b/src/buffer.C @@ -2203,8 +2203,8 @@ Inset * Buffer::getInsetFromID(int id_arg) const ParIterator Buffer::getParFromID(int id) const { #warning FIXME: const correctness! (Andre) - ParIterator it(const_cast(this)->par_iterator_begin()); - ParIterator end(const_cast(this)->par_iterator_end()); + ParIterator it = const_cast(this)->par_iterator_begin(); + ParIterator end = const_cast(this)->par_iterator_end(); #warning FIXME, perhaps this func should return a ParIterator? (Lgb) if (id < 0) { diff --git a/src/insets/insettabular.C b/src/insets/insettabular.C index cec7de014a..fa61c23ffb 100644 --- a/src/insets/insettabular.C +++ b/src/insets/insettabular.C @@ -1038,9 +1038,7 @@ Inset::RESULT InsetTabular::localDispatch(FuncRequest const & cmd) break; // no break here! case LFUN_DELETE: - setUndo(bv, Undo::DELETE, - bv->text->cursor.par(), - boost::next(bv->text->cursor.par())); + setUndo(bv, Undo::DELETE, bv->text->cursor.par()); cutSelection(bv->buffer()->params); updateLocal(bv, INIT); break; @@ -1123,9 +1121,7 @@ Inset::RESULT InsetTabular::localDispatch(FuncRequest const & cmd) } case LFUN_PASTE: if (hasPasteBuffer()) { - setUndo(bv, Undo::INSERT, - bv->text->cursor.par(), - boost::next(bv->text->cursor.par())); + setUndo(bv, Undo::INSERT, bv->text->cursor.par()); pasteSelection(bv); updateLocal(bv, INIT); break; @@ -1619,9 +1615,7 @@ void InsetTabular::setFont(BufferView * bv, LyXFont const & font, bool tall, setSelection(0, tabular->GetNumberOfCells() - 1); } if (hasSelection()) { - setUndo(bv, Undo::EDIT, - bv->text->cursor.par(), - boost::next(bv->text->cursor.par())); + setUndo(bv, Undo::EDIT, bv->text->cursor.par()); bool const frozen = undo_frozen; if (!frozen) freezeUndo(); @@ -1743,9 +1737,7 @@ void InsetTabular::tabularFeatures(BufferView * bv, sel_col_start = sel_col_end = tabular->column_of_cell(actcell); sel_row_start = sel_row_end = tabular->row_of_cell(actcell); } - setUndo(bv, Undo::FINISH, - bv->text->cursor.par(), - boost::next(bv->text->cursor.par())); + setUndo(bv, Undo::FINISH, bv->text->cursor.par()); int row = tabular->row_of_cell(actcell); int column = tabular->column_of_cell(actcell); diff --git a/src/insets/insettext.C b/src/insets/insettext.C index 9816292344..383597720f 100644 --- a/src/insets/insettext.C +++ b/src/insets/insettext.C @@ -1134,8 +1134,7 @@ Inset::RESULT InsetText::localDispatch(FuncRequest const & cmd) * true (on). */ #if 0 // This should not be needed here and is also WRONG! - setUndo(bv, Undo::INSERT, - lt->cursor.par(), boost::next(lt->cursor.par())); + setUndo(bv, Undo::INSERT, lt->cursor.par()); #endif bv->switchKeyMap(); if (lyxrc.auto_region_delete) { @@ -1853,7 +1852,7 @@ void InsetText::setFont(BufferView * bv, LyXFont const & font, bool toggleall, clear = true; } if (lt->selection.set()) { - setUndo(bv, Undo::EDIT, lt->cursor.par(), boost::next(lt->cursor.par())); + setUndo(bv, Undo::EDIT, lt->cursor.par()); } if (selectall) selectAll(bv); diff --git a/src/iterators.C b/src/iterators.C index b268aaf65c..011e44062b 100644 --- a/src/iterators.C +++ b/src/iterators.C @@ -161,6 +161,12 @@ size_t ParIterator::size() const } +ParagraphList & ParIterator::plist() const +{ + return *const_cast(pimpl_->positions.top().plist); +} + + bool operator==(ParIterator const & iter1, ParIterator const & iter2) { return iter1.pimpl_->positions == iter2.pimpl_->positions; diff --git a/src/iterators.h b/src/iterators.h index 08bfb9b176..8c607ee212 100644 --- a/src/iterators.h +++ b/src/iterators.h @@ -33,6 +33,8 @@ public: /// ParagraphList::iterator operator->() const; /// + ParagraphList & plist() const; + /// size_t size() const; /// friend diff --git a/src/text.C b/src/text.C index 436f94354e..632370f599 100644 --- a/src/text.C +++ b/src/text.C @@ -1465,7 +1465,7 @@ void LyXText::breakParagraph(ParagraphList & paragraphs, char keep_layout) && !layout->keepempty) return; - setUndo(bv(), Undo::FINISH, cursor.par(), boost::next(cursor.par())); + setUndo(bv(), Undo::FINISH, cursor.par()); // Always break behind a space // @@ -1570,7 +1570,7 @@ void LyXText::redoParagraph() // same Paragraph one to the right and make a rebreak void LyXText::insertChar(char c) { - setUndo(bv(), Undo::INSERT, cursor.par(), boost::next(cursor.par())); + setUndo(bv(), Undo::INSERT, cursor.par()); // When the free-spacing option is set for the current layout, // disable the double-space checking @@ -2120,7 +2120,7 @@ void LyXText::acceptChange() if (selection.start.par() == selection.end.par()) { LyXCursor & startc = selection.start; LyXCursor & endc = selection.end; - setUndo(bv(), Undo::INSERT, startc.par(), boost::next(startc.par())); + setUndo(bv(), Undo::INSERT, startc.par()); startc.par()->acceptChange(startc.pos(), endc.pos()); finishUndo(); clearSelection(); @@ -2139,8 +2139,7 @@ void LyXText::rejectChange() if (selection.start.par() == selection.end.par()) { LyXCursor & startc = selection.start; LyXCursor & endc = selection.end; - setUndo(bv(), Undo::INSERT, startc.par(), - boost::next(startc.par())); + setUndo(bv(), Undo::INSERT, startc.par()); startc.par()->rejectChange(startc.pos(), endc.pos()); finishUndo(); clearSelection(); @@ -2357,7 +2356,7 @@ void LyXText::changeCase(LyXText::TextCase action) lyx::Assert(from <= to); - setUndo(bv(), Undo::FINISH, from.par(), boost::next(to.par())); + setUndo(bv(), Undo::FINISH, from.par(), to.par()); pos_type pos = from.pos(); ParagraphList::iterator pit = from.par(); @@ -2427,8 +2426,7 @@ void LyXText::Delete() LyXCursor tmpcursor = cursor; // to make sure undo gets the right cursor position cursor = old_cursor; - setUndo(bv(), Undo::DELETE, - cursor.par(), boost::next(cursor.par())); + setUndo(bv(), Undo::DELETE, cursor.par()); cursor = tmpcursor; backspace(); } @@ -2487,7 +2485,7 @@ void LyXText::backspace() if (cursor.par() != ownerParagraphs().begin()) { setUndo(bv(), Undo::DELETE, boost::prior(cursor.par()), - boost::next(cursor.par())); + cursor.par()); } ParagraphList::iterator tmppit = cursor.par(); @@ -2553,8 +2551,7 @@ void LyXText::backspace() } else { // this is the code for a normal backspace, not pasting // any paragraphs - setUndo(bv(), Undo::DELETE, - cursor.par(), boost::next(cursor.par())); + setUndo(bv(), Undo::DELETE, cursor.par()); // We used to do cursorLeftIntern() here, but it is // not a good idea since it triggers the auto-delete // mechanism. So we do a cursorLeftIntern()-lite, diff --git a/src/text2.C b/src/text2.C index b34bc99063..e164b666f4 100644 --- a/src/text2.C +++ b/src/text2.C @@ -393,7 +393,7 @@ LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur, ++endpit; } - setUndo(bv(), Undo::EDIT, sstart_cur.par(), undoendpit); + setUndo(bv(), Undo::EDIT, sstart_cur.par(), boost::prior(undoendpit)); // ok we have a selection. This is always between sstart_cur // and sel_end cursor @@ -486,7 +486,7 @@ bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only) ParagraphList::iterator pastend = boost::next(end); if (!test_only) - setUndo(bv(), Undo::EDIT, start, pastend); + setUndo(bv(), Undo::EDIT, start, end); bool changed = false; @@ -588,8 +588,7 @@ void LyXText::setFont(LyXFont const & font, bool toggleall) // ok we have a selection. This is always between sel_start_cursor // and sel_end cursor - setUndo(bv(), Undo::EDIT, - selection.start.par(), boost::next(selection.end.par())); + setUndo(bv(), Undo::EDIT, selection.start.par(), selection.end.par()); freezeUndo(); cursor = selection.start; while (cursor.par() != selection.end.par() || @@ -991,7 +990,8 @@ void LyXText::setParagraph(bool line_top, bool line_bottom, ++endpit; } - setUndo(bv(), Undo::EDIT, selection.start.par(), undoendpit); + setUndo(bv(), Undo::EDIT, selection.start.par(), + boost::prior(undoendpit)); ParagraphList::iterator tmppit = selection.end.par(); @@ -1276,8 +1276,7 @@ void LyXText::insertInset(Inset * inset) { if (!cursor.par()->insetAllowed(inset->lyxCode())) return; - setUndo(bv(), Undo::FINISH, cursor.par(), - boost::next(cursor.par())); + setUndo(bv(), Undo::FINISH, cursor.par()); freezeUndo(); cursor.par()->insertInset(cursor.pos(), inset); // Just to rebreak and refresh correctly. @@ -1329,7 +1328,8 @@ void LyXText::cutSelection(bool doclear, bool realcut) ++endpit; } - setUndo(bv(), Undo::DELETE, selection.start.par(), undoendpit); + setUndo(bv(), Undo::DELETE, selection.start.par(), + boost::prior(undoendpit)); endpit = selection.end.par(); @@ -1401,8 +1401,7 @@ void LyXText::pasteSelection() if (!CutAndPaste::checkPastePossible()) return; - setUndo(bv(), Undo::INSERT, - cursor.par(), boost::next(cursor.par())); + setUndo(bv(), Undo::INSERT, cursor.par()); ParagraphList::iterator endpit; PitPosPair ppp; @@ -2264,7 +2263,8 @@ bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor) ++endpit; } - setUndo(bv(), Undo::DELETE, old_cursor.par(), endpit); + setUndo(bv(), Undo::DELETE, old_cursor.par(), + boost::prior(endpit)); cursor = tmpcursor; // delete old row @@ -2295,7 +2295,7 @@ bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor) ++endpit; } - setUndo(bv(), Undo::DELETE, old_cursor.par(), endpit); + setUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit)); cursor = tmpcursor; // delete old row diff --git a/src/text3.C b/src/text3.C index 25e64e602b..016abfe7bf 100644 --- a/src/text3.C +++ b/src/text3.C @@ -418,7 +418,7 @@ Inset::RESULT LyXText::dispatch(FuncRequest const & cmd) for (; tmp != end; ++tmp) { if (tmp->params().startOfAppendix()) { - setUndo(bv, Undo::EDIT, tmp, boost::next(tmp)); + setUndo(bv, Undo::EDIT, tmp); tmp->params().startOfAppendix(false); int tmpy; setHeightOfRow(getRow(tmp, 0, tmpy)); @@ -426,7 +426,7 @@ Inset::RESULT LyXText::dispatch(FuncRequest const & cmd) } } - setUndo(bv, Undo::EDIT, pit, boost::next(pit)); + setUndo(bv, Undo::EDIT, pit); pit->params().startOfAppendix(start); // we can set the refreshing parameters now diff --git a/src/text_funcs.C b/src/text_funcs.C index a958ef72b4..a5f4daf6fe 100644 --- a/src/text_funcs.C +++ b/src/text_funcs.C @@ -27,7 +27,7 @@ void transposeChars(LyXText & text, LyXCursor const & cursor) { ParagraphList::iterator tmppit = cursor.par(); - setUndo(text.bv(), Undo::FINISH, tmppit, boost::next(tmppit)); + setUndo(text.bv(), Undo::FINISH, tmppit); pos_type tmppos = cursor.pos(); diff --git a/src/undo_funcs.C b/src/undo_funcs.C index da7255979a..50a8dce312 100644 --- a/src/undo_funcs.C +++ b/src/undo_funcs.C @@ -47,15 +47,15 @@ LyXCursor const & undoCursor(BufferView * bv) * we are so it will return the first paragraph of the buffer or the * first paragraph of the textinset we're in. */ -ParagraphList undoParagraphs(BufferView * bv, int inset_id) +ParagraphList * undoParagraphs(BufferView * bv, int inset_id) { Inset * inset = bv->buffer()->getInsetFromID(inset_id); if (inset) { ParagraphList * result = inset->getParagraphs(0); if (result && !result->empty()) - return *result; + return result; } - return bv->text->ownerParagraphs(); + return &bv->text->ownerParagraphs(); } @@ -95,7 +95,7 @@ bool textHandleUndo(BufferView * bv, Undo & undo) num = -1; } } - t->setCursorIntern(undoParagraphs(bv, num).begin(), 0); + t->setCursorIntern(undoParagraphs(bv, num)->begin(), 0); } // Set the right(new) inset-owner of the paragraph if there is any. @@ -119,7 +119,7 @@ bool textHandleUndo(BufferView * bv, Undo & undo) deletepar = *before; ++deletepar; } else { - deletepar = undoParagraphs(bv, undo.number_of_inset_id).begin(); + deletepar = undoParagraphs(bv, undo.number_of_inset_id)->begin(); } // this surprisingly fills the undo! (Andre') size_t par = 0; @@ -160,7 +160,7 @@ bool textHandleUndo(BufferView * bv, Undo & undo) #warning FIXME //(&**before)->next(undopar); } else { - int id = undoParagraphs(bv, undo.number_of_inset_id).front().id(); + int id = undoParagraphs(bv, undo.number_of_inset_id)->begin()->id(); ParIterator op = bv->buffer()->getParFromID(id); if (op != end && op->inInset()) { #warning FIXME reimplementaion needed here @@ -175,7 +175,7 @@ bool textHandleUndo(BufferView * bv, Undo & undo) // have to substitue the second paragraph with the // first if the removed one is the first. if (before == end && behind != end) { - int id = undoParagraphs(bv, undo.number_of_inset_id).front().id(); + int id = undoParagraphs(bv, undo.number_of_inset_id)->begin()->id(); ParIterator op = bv->buffer()->getParFromID(id); if (op != end && op->inInset()) { #warning FIXME reimplementation needed here @@ -273,28 +273,57 @@ bool textHandleUndo(BufferView * bv, Undo & undo) bool createUndo(BufferView * bv, Undo::undo_kind kind, - ParagraphList::iterator itfirst, ParagraphList::iterator itbehind, + ParagraphList::iterator itfirst, ParagraphList::iterator itlast, shared_ptr & u) { - Paragraph * const first = &*itfirst; - Paragraph * const behind = &*itbehind; - lyx::Assert(first); - - int before_number = -1; - int behind_number = -1; - int inset_id = -1; - -#warning FIXME - //if (first->previous()) - // before_number = first->previous()->id(); - if (behind) - behind_number = behind->id(); - if (first->inInset()) - inset_id = first->inInset()->id(); - Buffer * b = bv->buffer(); - // Undo::EDIT and Undo::FINISH are + ParIterator it = b->par_iterator_begin(); + ParIterator null = b->par_iterator_end(); + ParIterator prev = null; + ParIterator before = null; + ParIterator first = null; + ParIterator last = null; + ParIterator behind = null; + + for (; it != null; ++it) { + if ((*it)->id() == itfirst->id()) { + first = it; + before = prev; + } else if ((*it)->id() == itlast->id()) { + last = it; + behind = last; + ++behind; + } + prev = it; + } + + if (last == null) + last = first; + + int const before_id = (before == null) ? -1 : (*before)->id(); + int const first_id = (first == null) ? -1 : (*first)->id(); + int const last_id = (last == null) ? -1 : (*last)->id(); + int const behind_id = (behind == null) ? -1 : (*behind)->id(); + int inset_id = (first->inInset()) ? first->inInset()->id() : -1; + + lyxerr << "\nbefore_id: " << before_id << "\n"; + lyxerr << "first_id: " << first_id << "\n"; + lyxerr << "last_id: " << last_id << "\n"; + lyxerr << "behind_id: " << behind_id << "\n"; + lyxerr << "inset_id: " << inset_id << "\n"; + + ParagraphList * plist = 0; + if (first != null) + plist = &first.plist(); + else if (behind != null) + plist = &behind.plist(); + else if (!plist) { + lyxerr << "plist from buffer (should this happen?)\n"; + plist = &b->paragraphs; + } + + // Undo::EDIT and Undo::FINISH are // always finished. (no overlapping there) // overlapping only with insert and delete inside one paragraph: // Nobody wants all removed character @@ -305,8 +334,8 @@ bool createUndo(BufferView * bv, Undo::undo_kind kind, // 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) { + b->undostack.top()->number_of_before_par == before_id && + b->undostack.top()->number_of_behind_par == behind_id) { // No undo needed. return false; } @@ -315,49 +344,23 @@ bool createUndo(BufferView * bv, Undo::undo_kind kind, // Create a new Undo. std::vector undo_pars; - Paragraph const * end = 0; - - if (behind) { -#warning FIXME - //end = behind->previous(); - }else { - end = first; -#warning FIXME - //while (end->next()) - // end = end->next(); - } - -#warning FIXME -// if (first && end && (first != end->next()) && -// ((before_number != behind_number) || -// ((before_number < 0) && (behind_number < 0)))) -// { -// undo_pars.push_back(new Paragraph(*first, true)); -// for (Paragraph * tmppar = first; tmppar != end && tmppar->next(); ) { -// tmppar = tmppar->next(); -// undo_pars.push_back(new Paragraph(*tmppar, true)); -// size_t const n = undo_pars.size(); -// undo_pars[n - 2]->next(undo_pars[n - 1]); -// undo_pars[n - 1]->previous(undo_pars[n - 2]); -// } -// undo_pars.back()->next(0); -// } + for (ParagraphList::iterator it = *first; it != *last; ++it) + undo_pars.push_back(new Paragraph(*it, true)); + undo_pars.push_back(new Paragraph(**last, true)); // A memory optimization: Just store the layout // information when only edit. - if (kind == Undo::EDIT) { + if (kind == Undo::EDIT) for (size_t i = 0, n = undo_pars.size(); i < n; ++i) undo_pars[i]->clearContents(); - } int cursor_par = undoCursor(bv).par()->id(); int cursor_pos = undoCursor(bv).pos(); - //lyxerr << "createUndo: inset_id: " << inset_id << " before_number: " - // << before_number << " behind_number: " << behind_number << "\n"; + lyxerr << "createUndo: inset_id: " << inset_id << " before_id: " + << before_id << " behind_id: " << behind_id << "\n"; u.reset(new Undo(kind, inset_id, - before_number, behind_number, - cursor_par, cursor_pos, undo_pars)); + before_id, behind_id, cursor_par, cursor_pos, undo_pars)); undo_finished = false; return true; @@ -392,7 +395,7 @@ bool textUndoOrRedo(BufferView * bv, if (first->next()) first = first->next(); } else - first = undoParagraphs(bv, undo->number_of_inset_id).begin(); + first = undoParagraphs(bv, undo->number_of_inset_id)->begin(); if (first) { shared_ptr u; ParIterator behind = b->getParFromID(undo->number_of_behind_par); @@ -452,28 +455,27 @@ bool textRedo(BufferView * bv) void setUndo(BufferView * bv, Undo::undo_kind kind, - ParagraphList::iterator first, ParagraphList::iterator behind) + ParagraphList::iterator first) { + setUndo(bv, kind, first, first); +} + + +void setUndo(BufferView * bv, Undo::undo_kind kind, + ParagraphList::iterator first, ParagraphList::iterator last) +{ +#warning DISABLED + return; if (!undo_frozen) { shared_ptr u; - if (createUndo(bv, kind, first, behind, u)) + if (createUndo(bv, kind, first, last, u)) bv->buffer()->undostack.push(u); bv->buffer()->redostack.clear(); } } -void setRedo(BufferView * bv, Undo::undo_kind kind, - ParagraphList::iterator first, ParagraphList::iterator 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(), - boost::next(bv->text->cursor.par())); + setUndo(bv, Undo::FINISH, bv->text->cursor.par()); } diff --git a/src/undo_funcs.h b/src/undo_funcs.h index 0633f64881..1590cb6a33 100644 --- a/src/undo_funcs.h +++ b/src/undo_funcs.h @@ -18,24 +18,23 @@ class BufferView; class Paragraph; /// returns false if no undo possible -extern bool textUndo(BufferView *); +bool textUndo(BufferView *); /// returns false if no redo possible -extern bool textRedo(BufferView *); +bool textRedo(BufferView *); /// makes sure the next operation will be stored -extern void finishUndo(); +void finishUndo(); /// Whilst undo is frozen, all actions do not get added /// to the undo stack -extern void freezeUndo(); +void freezeUndo(); /// Track undos again -extern void unFreezeUndo(); +void unFreezeUndo(); /// FIXME -extern void setUndo(BufferView *, Undo::undo_kind kind, - ParagraphList::iterator first, ParagraphList::iterator behind); +void setUndo(BufferView *, Undo::undo_kind kind, + ParagraphList::iterator first, ParagraphList::iterator last); +void setUndo(BufferView *, Undo::undo_kind kind, + ParagraphList::iterator first); /// FIXME -extern void setRedo(BufferView *, Undo::undo_kind kind, - ParagraphList::iterator first, ParagraphList::iterator behind); -/// FIXME -extern void setCursorParUndo(BufferView *); +void setCursorParUndo(BufferView *); /// Are we avoiding tracking undos currently ? extern bool undo_frozen;