Refactor Undo/Redo framework into an Undo class owned by a Buffer. This reduces header dependencies from Undo.h which was including lots of stuff previously. This also solves the bug where undo/redo were reset upon buffer switching. Now, each buffer has a truly independant undo/redo architecture.

In the future, when Cursor has been fixed to work correctly with multiple views of the same Buffer, we could transfer the Undo owner from Buffer to Cursor.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@21031 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Abdelrazak Younes 2007-10-18 11:51:17 +00:00
parent 01ac256b90
commit 55beda376a
23 changed files with 468 additions and 361 deletions

View File

@ -166,8 +166,6 @@ class Buffer::Impl
public: public:
Impl(Buffer & parent, FileName const & file, bool readonly); Impl(Buffer & parent, FileName const & file, bool readonly);
limited_stack<Undo> undostack;
limited_stack<Undo> redostack;
BufferParams params; BufferParams params;
LyXVC lyxvc; LyXVC lyxvc;
string temppath; string temppath;
@ -219,6 +217,9 @@ public:
/// ///
frontend::WorkAreaManager * wa_; frontend::WorkAreaManager * wa_;
///
Undo undo_;
}; };
@ -226,7 +227,7 @@ Buffer::Impl::Impl(Buffer & parent, FileName const & file, bool readonly_)
: lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_), : lyx_clean(true), bak_clean(true), unnamed(false), read_only(readonly_),
filename(file), file_fully_loaded(false), inset(params), filename(file), file_fully_loaded(false), inset(params),
toc_backend(&parent), embedded_files(&parent), timestamp_(0), toc_backend(&parent), embedded_files(&parent), timestamp_(0),
checksum_(0), wa_(0) checksum_(0), wa_(0), undo_(parent)
{ {
inset.setAutoBreakRows(true); inset.setAutoBreakRows(true);
lyxvc.buffer(&parent); lyxvc.buffer(&parent);
@ -304,30 +305,6 @@ Inset & Buffer::inset() const
} }
limited_stack<Undo> & Buffer::undostack()
{
return pimpl_->undostack;
}
limited_stack<Undo> const & Buffer::undostack() const
{
return pimpl_->undostack;
}
limited_stack<Undo> & Buffer::redostack()
{
return pimpl_->redostack;
}
limited_stack<Undo> const & Buffer::redostack() const
{
return pimpl_->redostack;
}
BufferParams & Buffer::params() BufferParams & Buffer::params()
{ {
return pimpl_->params; return pimpl_->params;
@ -405,6 +382,12 @@ EmbeddedFiles const & Buffer::embeddedFiles() const
return pimpl_->embedded_files; return pimpl_->embedded_files;
} }
Undo & Buffer::undo()
{
return pimpl_->undo_;
}
string const Buffer::getLatexName(bool const no_path) const string const Buffer::getLatexName(bool const no_path) const
{ {

View File

@ -29,6 +29,9 @@
namespace lyx { namespace lyx {
class BufferParams; class BufferParams;
// FIXME: Remove!
class BufferView;
class Cursor;
class EmbeddedFiles; class EmbeddedFiles;
class ErrorItem; class ErrorItem;
class ErrorList; class ErrorList;
@ -301,14 +304,6 @@ public:
/// ///
bool isMultiLingual() const; bool isMultiLingual() const;
/// Does this mean that this is buffer local?
limited_stack<Undo> & undostack();
limited_stack<Undo> const & undostack() const;
/// Does this mean that this is buffer local?
limited_stack<Undo> & redostack();
limited_stack<Undo> const & redostack() const;
/// ///
BufferParams & params(); BufferParams & params();
BufferParams const & params() const; BufferParams const & params() const;
@ -391,6 +386,8 @@ public:
EmbeddedFiles const & embeddedFiles() const; EmbeddedFiles const & embeddedFiles() const;
//@} //@}
Undo & undo();
/// This function is called when the buffer is changed. /// This function is called when the buffer is changed.
void changed() const; void changed() const;
/// This function is called when the buffer structure is changed. /// This function is called when the buffer structure is changed.

View File

@ -50,7 +50,6 @@
#include "TextClass.h" #include "TextClass.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "TexRow.h" #include "TexRow.h"
#include "Undo.h"
#include "VSpace.h" #include "VSpace.h"
#include "WordLangTuple.h" #include "WordLangTuple.h"
@ -273,7 +272,7 @@ void outline(OutlineOp mode, Cursor & cur)
pit_type const newpit = std::distance(bgn, dest); pit_type const newpit = std::distance(bgn, dest);
pit_type const len = std::distance(start, finish); pit_type const len = std::distance(start, finish);
pit_type const deletepit = pit + len; pit_type const deletepit = pit + len;
recordUndo(cur, Undo::ATOMIC, newpit, deletepit - 1); buf.undo().recordUndo(cur, ATOMIC_UNDO, newpit, deletepit - 1);
pars.insert(dest, start, finish); pars.insert(dest, start, finish);
start = boost::next(pars.begin(), deletepit); start = boost::next(pars.begin(), deletepit);
pit = newpit; pit = newpit;
@ -305,7 +304,7 @@ void outline(OutlineOp mode, Cursor & cur)
// One such was found: // One such was found:
pit_type newpit = std::distance(bgn, dest); pit_type newpit = std::distance(bgn, dest);
pit_type const len = std::distance(start, finish); pit_type const len = std::distance(start, finish);
recordUndo(cur, Undo::ATOMIC, pit, newpit - 1); buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, newpit - 1);
pars.insert(dest, start, finish); pars.insert(dest, start, finish);
start = boost::next(bgn, pit); start = boost::next(bgn, pit);
pit = newpit - len; pit = newpit - len;
@ -313,7 +312,7 @@ void outline(OutlineOp mode, Cursor & cur)
break; break;
} }
case OutlineIn: case OutlineIn:
recordUndo(cur); buf.undo().recordUndo(cur);
for (; lit != lend; ++lit) { for (; lit != lend; ++lit) {
if ((*lit)->toclevel == thistoclevel + 1 && if ((*lit)->toclevel == thistoclevel + 1 &&
start->layout()->labeltype == (*lit)->labeltype) { start->layout()->labeltype == (*lit)->labeltype) {
@ -323,7 +322,7 @@ void outline(OutlineOp mode, Cursor & cur)
} }
break; break;
case OutlineOut: case OutlineOut:
recordUndo(cur); buf.undo().recordUndo(cur);
for (; lit != lend; ++lit) { for (; lit != lend; ++lit) {
if ((*lit)->toclevel == thistoclevel - 1 && if ((*lit)->toclevel == thistoclevel - 1 &&
start->layout()->labeltype == (*lit)->labeltype) { start->layout()->labeltype == (*lit)->labeltype) {
@ -853,10 +852,10 @@ FuncStatus BufferView::getStatus(FuncRequest const & cmd)
switch (cmd.action) { switch (cmd.action) {
case LFUN_UNDO: case LFUN_UNDO:
flag.enabled(!buffer_.undostack().empty()); flag.enabled(!buffer_.undo().hasUndoStack());
break; break;
case LFUN_REDO: case LFUN_REDO:
flag.enabled(!buffer_.redostack().empty()); flag.enabled(!buffer_.undo().hasRedoStack());
break; break;
case LFUN_FILE_INSERT: case LFUN_FILE_INSERT:
case LFUN_FILE_INSERT_PLAINTEXT_PARA: case LFUN_FILE_INSERT_PLAINTEXT_PARA:
@ -1018,7 +1017,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_UNDO: case LFUN_UNDO:
cur.message(_("Undo")); cur.message(_("Undo"));
cur.clearSelection(); cur.clearSelection();
if (!textUndo(*this)) { if (!cur.textUndo()) {
cur.message(_("No further undo information")); cur.message(_("No further undo information"));
updateFlags = Update::None; updateFlags = Update::None;
} }
@ -1027,7 +1026,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
case LFUN_REDO: case LFUN_REDO:
cur.message(_("Redo")); cur.message(_("Redo"));
cur.clearSelection(); cur.clearSelection();
if (!textRedo(*this)) { if (!cur.textRedo()) {
cur.message(_("No further redo information")); cur.message(_("No further redo information"));
updateFlags = Update::None; updateFlags = Update::None;
} }
@ -1346,7 +1345,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
cur.reset(buffer_.inset()); cur.reset(buffer_.inset());
d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
//FIXME: what to do with cur.x_target()? //FIXME: what to do with cur.x_target()?
finishUndo(); cur.finishUndo();
// The metrics are already up to date. see scroll() // The metrics are already up to date. see scroll()
updateFlags = Update::None; updateFlags = Update::None;
break; break;
@ -1361,7 +1360,7 @@ Update::flags BufferView::dispatch(FuncRequest const & cmd)
// FIXME: We need to verify if the cursor stayed within an inset... // FIXME: We need to verify if the cursor stayed within an inset...
//cur.reset(buffer_.inset()); //cur.reset(buffer_.inset());
d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_); d->text_metrics_[&buffer_.text()].editXY(cur, p.x_, p.y_);
finishUndo(); cur.finishUndo();
while (cur.depth() > initial_depth) { while (cur.depth() > initial_depth) {
cur.forwardInset(); cur.forwardInset();
} }
@ -1748,7 +1747,7 @@ bool BufferView::mouseSetCursor(Cursor & cur, bool select)
else else
d->cursor_.clearSelection(); d->cursor_.clearSelection();
finishUndo(); d->cursor_.finishUndo();
return update; return update;
} }
@ -1973,7 +1972,7 @@ void BufferView::menuInsertLyXFile(string const & filenm)
ErrorList & el = buffer_.errorList("Parse"); ErrorList & el = buffer_.errorList("Parse");
// Copy the inserted document error list into the current buffer one. // Copy the inserted document error list into the current buffer one.
el = buf.errorList("Parse"); el = buf.errorList("Parse");
recordUndo(d->cursor_); buffer_.undo().recordUndo(d->cursor_);
cap::pasteParagraphList(d->cursor_, buf.paragraphs(), cap::pasteParagraphList(d->cursor_, buf.paragraphs(),
buf.params().getTextClassPtr(), el); buf.params().getTextClassPtr(), el);
res = _("Document %1$s inserted."); res = _("Document %1$s inserted.");
@ -2261,12 +2260,11 @@ void BufferView::insertPlaintextFile(string const & f, bool asParagraph)
Cursor & cur = cursor(); Cursor & cur = cursor();
cap::replaceSelection(cur); cap::replaceSelection(cur);
recordUndo(cur); buffer_.undo().recordUndo(cur);
if (asParagraph) if (asParagraph)
cur.innerText()->insertStringAsParagraphs(cur, tmpstr); cur.innerText()->insertStringAsParagraphs(cur, tmpstr);
else else
cur.innerText()->insertStringAsLines(cur, tmpstr); cur.innerText()->insertStringAsLines(cur, tmpstr);
} }
} // namespace lyx } // namespace lyx

View File

@ -1552,4 +1552,78 @@ void Cursor::setCurrentFont()
} }
} }
bool Cursor::textUndo()
{
DocIterator dit = *this;
// Undo::textUndo() will modify dit.
if (!bv_->buffer().undo().textUndo(dit))
return false;
// Set cursor
setCursor(dit);
selection() = false;
resetAnchor();
fixIfBroken();
return true;
}
bool Cursor::textRedo()
{
DocIterator dit = *this;
// Undo::textRedo() will modify dit.
if (!bv_->buffer().undo().textRedo(dit))
return false;
// Set cursor
setCursor(dit);
selection() = false;
resetAnchor();
fixIfBroken();
return true;
}
void Cursor::finishUndo()
{
bv_->buffer().undo().finishUndo();
}
void Cursor::recordUndo(UndoKind kind, pit_type from, pit_type to)
{
bv_->buffer().undo().recordUndo(*this, kind, from, to);
}
void Cursor::recordUndo(UndoKind kind, pit_type from)
{
bv_->buffer().undo().recordUndo(*this, kind, from);
}
void Cursor::recordUndo(UndoKind kind)
{
bv_->buffer().undo().recordUndo(*this, kind);
}
void Cursor::recordUndoInset(UndoKind kind)
{
bv_->buffer().undo().recordUndoInset(*this, kind);
}
void Cursor::recordUndoFullDocument()
{
bv_->buffer().undo().recordUndoFullDocument(*this);
}
void Cursor::recordUndoSelection()
{
bv_->buffer().undo().recordUndo(*this, ATOMIC_UNDO,
selBegin().pit(), selEnd().pit());
}
} // namespace lyx } // namespace lyx

View File

@ -15,6 +15,7 @@
#include "DispatchResult.h" #include "DispatchResult.h"
#include "DocIterator.h" #include "DocIterator.h"
#include "Font.h" #include "Font.h"
#include "Undo.h"
#include <iosfwd> #include <iosfwd>
#include <vector> #include <vector>
@ -189,6 +190,33 @@ public:
/// output /// output
friend std::ostream & operator<<(std::ostream & os, Cursor const & cur); friend std::ostream & operator<<(std::ostream & os, Cursor const & cur);
///
bool textUndo();
///
bool textRedo();
/// makes sure the next operation will be stored
void finishUndo();
/// The general case: prepare undo for an arbitrary range.
void recordUndo(UndoKind kind, pit_type from, pit_type to);
/// Convenience: prepare undo for the range between 'from' and cursor.
void recordUndo(UndoKind kind, pit_type from);
/// Convenience: prepare undo for the single paragraph or cell
/// containing the cursor
void recordUndo(UndoKind kind = ATOMIC_UNDO);
/// Convenience: prepare undo for the inset containing the cursor
void recordUndoInset(UndoKind kind = ATOMIC_UNDO);
/// Convenience: prepare undo for the whole buffer
void recordUndoFullDocument();
/// Convenience: prepare undo for the selected paragraphs
void recordUndoSelection();
public: public:
/// ///
BufferView * bv_; BufferView * bv_;

View File

@ -518,7 +518,7 @@ void cutSelection(Cursor & cur, bool doclear, bool realcut)
saveSelection(cur); saveSelection(cur);
// make sure that the depth behind the selection are restored, too // make sure that the depth behind the selection are restored, too
recordUndoSelection(cur); cur.recordUndoSelection();
pit_type begpit = cur.selBegin().pit(); pit_type begpit = cur.selBegin().pit();
pit_type endpit = cur.selEnd().pit(); pit_type endpit = cur.selEnd().pit();
@ -566,11 +566,11 @@ void cutSelection(Cursor & cur, bool doclear, bool realcut)
if (cur.selBegin().idx() != cur.selEnd().idx()) { if (cur.selBegin().idx() != cur.selEnd().idx()) {
// The current selection spans more than one cell. // The current selection spans more than one cell.
// Record all cells // Record all cells
recordUndoInset(cur); cur.recordUndoInset();
} else { } else {
// Record only the current cell to avoid a jumping // Record only the current cell to avoid a jumping
// cursor after undo // cursor after undo
recordUndo(cur); cur.recordUndo();
} }
if (realcut) if (realcut)
copySelection(cur); copySelection(cur);
@ -733,7 +733,7 @@ void pasteFromStack(Cursor & cur, ErrorList & errorList, size_t sel_index)
if (!checkPastePossible(sel_index)) if (!checkPastePossible(sel_index))
return; return;
recordUndo(cur); cur.recordUndo();
pasteParagraphList(cur, theCuts[sel_index].first, pasteParagraphList(cur, theCuts[sel_index].first,
theCuts[sel_index].second, errorList); theCuts[sel_index].second, errorList);
cur.setSelection(); cur.setSelection();
@ -757,7 +757,7 @@ void pasteClipboard(Cursor & cur, ErrorList & errorList, bool asParagraphs)
Buffer buffer("", false); Buffer buffer("", false);
buffer.setUnnamed(true); buffer.setUnnamed(true);
if (buffer.readString(lyx)) { if (buffer.readString(lyx)) {
recordUndo(cur); cur.recordUndo();
pasteParagraphList(cur, buffer.paragraphs(), pasteParagraphList(cur, buffer.paragraphs(),
buffer.params().getTextClassPtr(), errorList); buffer.params().getTextClassPtr(), errorList);
cur.setSelection(); cur.setSelection();
@ -770,7 +770,7 @@ void pasteClipboard(Cursor & cur, ErrorList & errorList, bool asParagraphs)
docstring const text = theClipboard().getAsText(); docstring const text = theClipboard().getAsText();
if (text.empty()) if (text.empty())
return; return;
recordUndo(cur); cur.recordUndo();
if (asParagraphs) if (asParagraphs)
cur.text()->insertStringAsParagraphs(cur, text); cur.text()->insertStringAsParagraphs(cur, text);
else else
@ -782,7 +782,7 @@ void pasteSelection(Cursor & cur, ErrorList & errorList)
{ {
if (selectionBuffer.empty()) if (selectionBuffer.empty())
return; return;
recordUndo(cur); cur.recordUndo();
pasteParagraphList(cur, selectionBuffer[0].first, pasteParagraphList(cur, selectionBuffer[0].first,
selectionBuffer[0].second, errorList); selectionBuffer[0].second, errorList);
} }
@ -790,7 +790,7 @@ void pasteSelection(Cursor & cur, ErrorList & errorList)
void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwards) void replaceSelectionWithString(Cursor & cur, docstring const & str, bool backwards)
{ {
recordUndo(cur); cur.recordUndo();
DocIterator selbeg = cur.selectionBegin(); DocIterator selbeg = cur.selectionBegin();
// Get font setting before we cut // Get font setting before we cut

View File

@ -59,7 +59,6 @@
#include "Session.h" #include "Session.h"
#include "TextClassList.h" #include "TextClassList.h"
#include "ToolbarBackend.h" #include "ToolbarBackend.h"
#include "Undo.h"
#include "insets/InsetBox.h" #include "insets/InsetBox.h"
#include "insets/InsetBranch.h" #include "insets/InsetBranch.h"
@ -1798,7 +1797,9 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
Buffer * buffer = lyx_view_->buffer(); Buffer * buffer = lyx_view_->buffer();
TextClassPtr oldClass = buffer->params().getTextClassPtr(); TextClassPtr oldClass = buffer->params().getTextClassPtr();
recordUndoFullDocument(view());
Cursor & cur = view()->cursor();
cur.recordUndoFullDocument();
istringstream ss(argument); istringstream ss(argument);
Lexer lex(0,0); Lexer lex(0,0);
@ -1818,7 +1819,6 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
lyx_view_->buffer()->params().getEngine(); lyx_view_->buffer()->params().getEngine();
if (oldEngine != newEngine) { if (oldEngine != newEngine) {
Cursor & cur = view()->cursor();
FuncRequest fr(LFUN_INSET_REFRESH); FuncRequest fr(LFUN_INSET_REFRESH);
Inset & inset = lyx_view_->buffer()->inset(); Inset & inset = lyx_view_->buffer()->inset();
@ -1837,7 +1837,7 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
BOOST_ASSERT(lyx_view_); BOOST_ASSERT(lyx_view_);
Buffer * buffer = lyx_view_->buffer(); Buffer * buffer = lyx_view_->buffer();
TextClassPtr oldClass = buffer->params().getTextClassPtr(); TextClassPtr oldClass = buffer->params().getTextClassPtr();
recordUndoFullDocument(view()); view()->cursor().recordUndoFullDocument();
buffer->params().clearLayoutModules(); buffer->params().clearLayoutModules();
updateLayout(oldClass, buffer); updateLayout(oldClass, buffer);
updateFlags = Update::Force | Update::FitCursor; updateFlags = Update::Force | Update::FitCursor;
@ -1848,7 +1848,7 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
BOOST_ASSERT(lyx_view_); BOOST_ASSERT(lyx_view_);
Buffer * buffer = lyx_view_->buffer(); Buffer * buffer = lyx_view_->buffer();
TextClassPtr oldClass = buffer->params().getTextClassPtr(); TextClassPtr oldClass = buffer->params().getTextClassPtr();
recordUndoFullDocument(view()); view()->cursor().recordUndoFullDocument();
buffer->params().addLayoutModule(argument); buffer->params().addLayoutModule(argument);
updateLayout(oldClass, buffer); updateLayout(oldClass, buffer);
updateFlags = Update::Force | Update::FitCursor; updateFlags = Update::Force | Update::FitCursor;
@ -1876,7 +1876,7 @@ void LyXFunc::dispatch(FuncRequest const & cmd)
//Save the old, possibly modular, layout for use in conversion. //Save the old, possibly modular, layout for use in conversion.
TextClassPtr oldClass = buffer->params().getTextClassPtr(); TextClassPtr oldClass = buffer->params().getTextClassPtr();
recordUndoFullDocument(view()); view()->cursor().recordUndoFullDocument();
buffer->params().setBaseClass(new_class); buffer->params().setBaseClass(new_class);
updateLayout(oldClass, buffer); updateLayout(oldClass, buffer);
updateFlags = Update::Force | Update::FitCursor; updateFlags = Update::Force | Update::FitCursor;

View File

@ -44,7 +44,6 @@
#include "Paragraph.h" #include "Paragraph.h"
#include "paragraph_funcs.h" #include "paragraph_funcs.h"
#include "ParagraphParameters.h" #include "ParagraphParameters.h"
#include "Undo.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "VSpace.h" #include "VSpace.h"
#include "WordLangTuple.h" #include "WordLangTuple.h"
@ -438,7 +437,7 @@ void Text::insertChar(Cursor & cur, char_type c)
BOOST_ASSERT(this == cur.text()); BOOST_ASSERT(this == cur.text());
BOOST_ASSERT(c != Paragraph::META_INSET); BOOST_ASSERT(c != Paragraph::META_INSET);
recordUndo(cur, Undo::INSERT); cur.recordUndo(INSERT_UNDO);
TextMetrics const & tm = cur.bv().textMetrics(this); TextMetrics const & tm = cur.bv().textMetrics(this);
Buffer const & buffer = cur.buffer(); Buffer const & buffer = cur.buffer();
@ -566,11 +565,11 @@ void Text::insertChar(Cursor & cur, char_type c)
// cur.updateFlags(Update::Force); // cur.updateFlags(Update::Force);
setCursor(cur.top(), cur.pit(), cur.pos() + 1); setCursor(cur.top(), cur.pit(), cur.pos() + 1);
charInserted(); charInserted(cur);
} }
void Text::charInserted() void Text::charInserted(Cursor & cur)
{ {
// Here we call finishUndo for every 20 characters inserted. // Here we call finishUndo for every 20 characters inserted.
// This is from my experience how emacs does it. (Lgb) // This is from my experience how emacs does it. (Lgb)
@ -578,7 +577,7 @@ void Text::charInserted()
if (counter < 20) { if (counter < 20) {
++counter; ++counter;
} else { } else {
finishUndo(); cur.finishUndo();
counter = 0; counter = 0;
} }
} }
@ -664,7 +663,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
if (!cur.selection()) if (!cur.selection())
return; return;
recordUndoSelection(cur, Undo::ATOMIC); cur.recordUndoSelection();
pit_type begPit = cur.selectionBegin().pit(); pit_type begPit = cur.selectionBegin().pit();
pit_type endPit = cur.selectionEnd().pit(); pit_type endPit = cur.selectionEnd().pit();
@ -753,7 +752,7 @@ void Text::acceptOrRejectChanges(Cursor & cur, ChangeOp op)
// //
finishUndo(); cur.finishUndo();
cur.clearSelection(); cur.clearSelection();
setCursorIntern(cur, begPit, begPos); setCursorIntern(cur, begPit, begPos);
cur.updateFlags(Update::Force); cur.updateFlags(Update::Force);
@ -851,7 +850,7 @@ void Text::changeCase(Cursor & cur, Text::TextCase action)
cursorRightOneWord(cur); cursorRightOneWord(cur);
} }
recordUndoSelection(cur, Undo::ATOMIC); cur.recordUndoSelection();
pit_type begPit = from.pit(); pit_type begPit = from.pit();
pit_type endPit = to.pit(); pit_type endPit = to.pit();
@ -953,7 +952,7 @@ bool Text::handleBibitems(Cursor & cur)
} }
Paragraph const & prevpar = prevcur.paragraph(); Paragraph const & prevpar = prevcur.paragraph();
if (cur.pit() > 0 && par.layout() == prevpar.layout()) { if (cur.pit() > 0 && par.layout() == prevpar.layout()) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit()); cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
mergeParagraph(bufparams, cur.text()->paragraphs(), mergeParagraph(bufparams, cur.text()->paragraphs(),
prevcur.pit()); prevcur.pit());
updateLabels(cur.buffer()); updateLabels(cur.buffer());
@ -978,7 +977,7 @@ bool Text::erase(Cursor & cur)
if (cur.pos() != cur.lastpos()) { if (cur.pos() != cur.lastpos()) {
// this is the code for a normal delete, not pasting // this is the code for a normal delete, not pasting
// any paragraphs // any paragraphs
recordUndo(cur, Undo::DELETE); cur.recordUndo(DELETE_UNDO);
if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges)) { if(!par.eraseChar(cur.pos(), cur.buffer().params().trackChanges)) {
// the character has been logically deleted only => skip it // the character has been logically deleted only => skip it
cur.top().forwardPos(); cur.top().forwardPos();
@ -1032,14 +1031,14 @@ bool Text::backspacePos0(Cursor & cur)
// is it an empty paragraph? // is it an empty paragraph?
if (cur.lastpos() == 0 if (cur.lastpos() == 0
|| (cur.lastpos() == 1 && par.isSeparator(0))) { || (cur.lastpos() == 1 && par.isSeparator(0))) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit()); cur.recordUndo(ATOMIC_UNDO, prevcur.pit(), cur.pit());
plist.erase(boost::next(plist.begin(), cur.pit())); plist.erase(boost::next(plist.begin(), cur.pit()));
needsUpdate = true; needsUpdate = true;
} }
// is previous par empty? // is previous par empty?
else if (prevcur.lastpos() == 0 else if (prevcur.lastpos() == 0
|| (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) { || (prevcur.lastpos() == 1 && prevpar.isSeparator(0))) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit(), cur.pit()); cur.recordUndo(ATOMIC_UNDO, prevcur.pit(), cur.pit());
plist.erase(boost::next(plist.begin(), prevcur.pit())); plist.erase(boost::next(plist.begin(), prevcur.pit()));
needsUpdate = true; needsUpdate = true;
} }
@ -1049,7 +1048,7 @@ bool Text::backspacePos0(Cursor & cur)
// Correction: Pasting is always allowed with standard-layout // Correction: Pasting is always allowed with standard-layout
else if (par.layout() == prevpar.layout() else if (par.layout() == prevpar.layout()
|| par.layout() == tclass.defaultLayout()) { || par.layout() == tclass.defaultLayout()) {
recordUndo(cur, Undo::ATOMIC, prevcur.pit()); cur.recordUndo(ATOMIC_UNDO, prevcur.pit());
mergeParagraph(bufparams, plist, prevcur.pit()); mergeParagraph(bufparams, plist, prevcur.pit());
needsUpdate = true; needsUpdate = true;
} }
@ -1085,7 +1084,7 @@ bool Text::backspace(Cursor & cur)
} else { } else {
// this is the code for a normal backspace, not pasting // this is the code for a normal backspace, not pasting
// any paragraphs // any paragraphs
recordUndo(cur, Undo::DELETE); cur.recordUndo(DELETE_UNDO);
// We used to do cursorLeftIntern() here, but it is // We used to do cursorLeftIntern() here, but it is
// not a good idea since it triggers the auto-delete // not a good idea since it triggers the auto-delete
// mechanism. So we do a cursorLeftIntern()-lite, // mechanism. So we do a cursorLeftIntern()-lite,
@ -1115,7 +1114,7 @@ bool Text::dissolveInset(Cursor & cur) {
if (isMainText(cur.bv().buffer()) || cur.inset().nargs() != 1) if (isMainText(cur.bv().buffer()) || cur.inset().nargs() != 1)
return false; return false;
recordUndoInset(cur); cur.recordUndoInset();
cur.selHandle(false); cur.selHandle(false);
// save position // save position
pos_type spos = cur.pos(); pos_type spos = cur.pos();
@ -1456,7 +1455,7 @@ void Text::charsTranspose(Cursor & cur)
// Track the changes if Change Tracking is enabled. // Track the changes if Change Tracking is enabled.
bool const trackChanges = cur.buffer().params().trackChanges; bool const trackChanges = cur.buffer().params().trackChanges;
recordUndo(cur); cur.recordUndo();
par.eraseChar(pos2, trackChanges); par.eraseChar(pos2, trackChanges);
par.eraseChar(pos1, trackChanges); par.eraseChar(pos1, trackChanges);

View File

@ -287,7 +287,7 @@ private:
/// handle the case where bibitems were deleted /// handle the case where bibitems were deleted
bool handleBibitems(Cursor & cur); bool handleBibitems(Cursor & cur);
/// ///
void charInserted(); void charInserted(Cursor & cur);
/// set 'number' font property /// set 'number' font property
void number(Cursor & cur); void number(Cursor & cur);

View File

@ -48,7 +48,6 @@
#include "Server.h" #include "Server.h"
#include "ServerSocket.h" #include "ServerSocket.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "Undo.h"
#include "VSpace.h" #include "VSpace.h"
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
@ -292,7 +291,7 @@ void Text::changeDepth(Cursor & cur, DEPTH_CHANGE type)
BOOST_ASSERT(this == cur.text()); BOOST_ASSERT(this == cur.text());
pit_type const beg = cur.selBegin().pit(); pit_type const beg = cur.selBegin().pit();
pit_type const end = cur.selEnd().pit() + 1; pit_type const end = cur.selEnd().pit() + 1;
recordUndoSelection(cur); cur.recordUndoSelection();
int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0); int max_depth = (beg != 0 ? pars_[beg - 1].getMaxDepthAfter() : 0);
for (pit_type pit = beg; pit != end; ++pit) { for (pit_type pit = beg; pit != end; ++pit) {
@ -340,7 +339,7 @@ void Text::setFont(Cursor & cur, Font const & font, bool toggleall)
return; return;
// Ok, we have a selection. // Ok, we have a selection.
recordUndoSelection(cur); cur.recordUndoSelection();
setFont(cur.bv(), cur.selectionBegin().top(), setFont(cur.bv(), cur.selectionBegin().top(),
cur.selectionEnd().top(), font, toggleall); cur.selectionEnd().top(), font, toggleall);
@ -812,7 +811,7 @@ bool Text::deleteEmptyParagraphMechanism(Cursor & cur,
if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) { if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
// Delete old par. // Delete old par.
recordUndo(old, Undo::ATOMIC, old.recordUndo(ATOMIC_UNDO,
max(old.pit() - 1, pit_type(0)), max(old.pit() - 1, pit_type(0)),
min(old.pit() + 1, old.lastpit())); min(old.pit() + 1, old.lastpit()));
ParagraphList & plist = old.text()->paragraphs(); ParagraphList & plist = old.text()->paragraphs();
@ -893,13 +892,13 @@ void Text::deleteEmptyParagraphMechanism(pit_type first, pit_type last, bool tra
void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const void Text::recUndo(Cursor & cur, pit_type first, pit_type last) const
{ {
recordUndo(cur, Undo::ATOMIC, first, last); cur.recordUndo(ATOMIC_UNDO, first, last);
} }
void Text::recUndo(Cursor & cur, pit_type par) const void Text::recUndo(Cursor & cur, pit_type par) const
{ {
recordUndo(cur, Undo::ATOMIC, par, par); cur.recordUndo(ATOMIC_UNDO, par, par);
} }
} // namespace lyx } // namespace lyx

View File

@ -44,7 +44,6 @@
#include "paragraph_funcs.h" #include "paragraph_funcs.h"
#include "ParagraphParameters.h" #include "ParagraphParameters.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "Undo.h"
#include "VSpace.h" #include "VSpace.h"
#include "ParIterator.h" #include "ParIterator.h"
@ -118,14 +117,14 @@ static void moveCursor(Cursor & cur, bool selecting)
static void finishChange(Cursor & cur, bool selecting) static void finishChange(Cursor & cur, bool selecting)
{ {
finishUndo(); cur.finishUndo();
moveCursor(cur, selecting); moveCursor(cur, selecting);
} }
static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display) static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display)
{ {
recordUndo(cur); cur.recordUndo();
docstring sel = cur.selectionAsString(false); docstring sel = cur.selectionAsString(false);
// It may happen that sel is empty but there is a selection // It may happen that sel is empty but there is a selection
@ -176,7 +175,7 @@ static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display)
static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind) static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind)
{ {
recordUndo(cur); cur.recordUndo();
cap::replaceSelection(cur); cap::replaceSelection(cur);
cur.insert(new InsetSpecialChar(kind)); cur.insert(new InsetSpecialChar(kind));
cur.posRight(); cur.posRight();
@ -190,7 +189,7 @@ static bool doInsertInset(Cursor & cur, Text * text,
if (!inset) if (!inset)
return false; return false;
recordUndo(cur); cur.recordUndo();
if (cmd.action == LFUN_INDEX_INSERT) { if (cmd.action == LFUN_INDEX_INSERT) {
docstring ds = support::subst(text->getStringToIndex(cur), '\n', ' '); docstring ds = support::subst(text->getStringToIndex(cur), '\n', ' ');
text->insertInset(cur, inset); text->insertInset(cur, inset);
@ -290,7 +289,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_PARAGRAPH_MOVE_DOWN: { case LFUN_PARAGRAPH_MOVE_DOWN: {
pit_type const pit = cur.pit(); pit_type const pit = cur.pit();
recUndo(cur, pit, pit + 1); recUndo(cur, pit, pit + 1);
finishUndo(); cur.finishUndo();
std::swap(pars_[pit], pars_[pit + 1]); std::swap(pars_[pit], pars_[pit + 1]);
updateLabels(cur.buffer()); updateLabels(cur.buffer());
needsUpdate = true; needsUpdate = true;
@ -301,7 +300,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_PARAGRAPH_MOVE_UP: { case LFUN_PARAGRAPH_MOVE_UP: {
pit_type const pit = cur.pit(); pit_type const pit = cur.pit();
recUndo(cur, pit - 1, pit); recUndo(cur, pit - 1, pit);
finishUndo(); cur.finishUndo();
std::swap(pars_[pit], pars_[pit - 1]); std::swap(pars_[pit], pars_[pit - 1]);
updateLabels(cur.buffer()); updateLabels(cur.buffer());
--cur.pit(); --cur.pit();
@ -325,7 +324,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
} }
} }
recordUndo(cur); cur.recordUndo();
par.params().startOfAppendix(start); par.params().startOfAppendix(start);
// we can set the refreshing parameters now // we can set the refreshing parameters now
@ -508,7 +507,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
// this avoids a double undo // this avoids a double undo
// FIXME: should not be needed, ideally // FIXME: should not be needed, ideally
if (!cur.selection()) if (!cur.selection())
recordUndo(cur); cur.recordUndo();
cap::replaceSelection(cur); cap::replaceSelection(cur);
cur.insert(new InsetNewline); cur.insert(new InsetNewline);
cur.posRight(); cur.posRight();
@ -639,7 +638,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
} }
case LFUN_INSET_INSERT: { case LFUN_INSET_INSERT: {
recordUndo(cur); cur.recordUndo();
Inset * inset = createInset(bv, cmd); Inset * inset = createInset(bv, cmd);
if (inset) { if (inset) {
// FIXME (Abdel 01/02/2006): // FIXME (Abdel 01/02/2006):
@ -740,7 +739,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
} }
bv->buffer().errors("Paste"); bv->buffer().errors("Paste");
cur.clearSelection(); // bug 393 cur.clearSelection(); // bug 393
finishUndo(); cur.finishUndo();
break; break;
case LFUN_CUT: case LFUN_CUT:
@ -870,7 +869,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
// this avoids a double undo // this avoids a double undo
// FIXME: should not be needed, ideally // FIXME: should not be needed, ideally
if (!cur.selection()) if (!cur.selection())
recordUndo(cur); cur.recordUndo();
cap::replaceSelection(cur); cap::replaceSelection(cur);
pos = cur.pos(); pos = cur.pos();
char_type c; char_type c;
@ -947,7 +946,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
bv->buffer().errorList("Paste")); bv->buffer().errorList("Paste"));
bv->buffer().errors("Paste"); bv->buffer().errors("Paste");
bv->buffer().markDirty(); bv->buffer().markDirty();
finishUndo(); bv->cursor().finishUndo();
} else { } else {
lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph")); lyx::dispatch(FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph"));
} }
@ -1200,7 +1199,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
Inset * inset = createInset(&cur.bv(), cmd); Inset * inset = createInset(&cur.bv(), cmd);
if (!inset) if (!inset)
break; break;
recordUndo(cur); cur.recordUndo();
cur.clearSelection(); cur.clearSelection();
insertInset(cur, inset); insertInset(cur, inset);
// Show the dialog for the nomenclature entry, since the // Show the dialog for the nomenclature entry, since the
@ -1437,7 +1436,7 @@ void Text::dispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_FLOAT_LIST: { case LFUN_FLOAT_LIST: {
TextClass const & tclass = bv->buffer().params().getTextClass(); TextClass const & tclass = bv->buffer().params().getTextClass();
if (tclass.floats().typeExist(to_utf8(cmd.argument()))) { if (tclass.floats().typeExist(to_utf8(cmd.argument()))) {
recordUndo(cur); cur.recordUndo();
if (cur.selection()) if (cur.selection())
cutSelection(cur, true, false); cutSelection(cur, true, false);
breakParagraph(cur); breakParagraph(cur);
@ -1958,7 +1957,7 @@ void Text::pasteString(Cursor & cur, docstring const & clip,
{ {
cur.clearSelection(); cur.clearSelection();
if (!clip.empty()) { if (!clip.empty()) {
recordUndo(cur); cur.recordUndo();
if (asParagraphs) if (asParagraphs)
insertStringAsParagraphs(cur, clip); insertStringAsParagraphs(cur, clip);
else else

View File

@ -38,7 +38,6 @@
#include "ParIterator.h" #include "ParIterator.h"
#include "rowpainter.h" #include "rowpainter.h"
#include "Text.h" #include "Text.h"
#include "Undo.h"
#include "VSpace.h" #include "VSpace.h"
#include "frontends/FontMetrics.h" #include "frontends/FontMetrics.h"
@ -1592,7 +1591,7 @@ void TextMetrics::cursorPrevious(Cursor & cur)
// simplest solution is to move to the previous row instead. // simplest solution is to move to the previous row instead.
cur.dispatch(FuncRequest(cur.selection()? LFUN_UP_SELECT: LFUN_UP)); cur.dispatch(FuncRequest(cur.selection()? LFUN_UP_SELECT: LFUN_UP));
finishUndo(); cur.finishUndo();
cur.updateFlags(Update::Force | Update::FitCursor); cur.updateFlags(Update::Force | Update::FitCursor);
} }
@ -1612,7 +1611,7 @@ void TextMetrics::cursorNext(Cursor & cur)
cur.dispatch( cur.dispatch(
FuncRequest(cur.selection()? LFUN_DOWN_SELECT: LFUN_DOWN)); FuncRequest(cur.selection()? LFUN_DOWN_SELECT: LFUN_DOWN));
finishUndo(); cur.finishUndo();
cur.updateFlags(Update::Force | Update::FitCursor); cur.updateFlags(Update::Force | Update::FitCursor);
} }

View File

@ -8,6 +8,7 @@
* \author John Levon * \author John Levon
* \author André Pönitz * \author André Pönitz
* \author Jürgen Vigna * \author Jürgen Vigna
* \author Abdelrazak Younes
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
@ -17,13 +18,13 @@
#include "Undo.h" #include "Undo.h"
#include "Buffer.h" #include "Buffer.h"
#include "BufferParams.h"
#include "buffer_funcs.h" #include "buffer_funcs.h"
#include "Cursor.h"
#include "debug.h" #include "debug.h"
#include "BufferView.h" #include "DocIterator.h"
#include "Text.h"
#include "Paragraph.h" #include "Paragraph.h"
#include "ParagraphList.h" #include "ParagraphList.h"
#include "Text.h"
#include "mathed/MathSupport.h" #include "mathed/MathSupport.h"
#include "mathed/MathData.h" #include "mathed/MathData.h"
@ -32,20 +33,117 @@
#include <algorithm> #include <algorithm>
using std::advance;
using std::endl;
namespace lyx { namespace lyx {
using std::advance;
using std::endl; /**
These are the elements put on the undo stack. Each object contains complete
paragraphs from some cell and sufficient information to restore the cursor
state.
The cell is given by a DocIterator pointing to this cell, the 'interesting'
range of paragraphs by counting them from begin and end of cell,
respectively.
The cursor is also given as DocIterator and should point to some place in
the stored paragraph range. In case of math, we simply store the whole
cell, as there usually is just a simple paragraph in a cell.
The idea is to store the contents of 'interesting' paragraphs in some
structure ('Undo') _before_ it is changed in some edit operation.
Obviously, the stored ranged should be as small as possible. However, it
there is a lower limit: The StableDocIterator pointing stored in the undo
class must be valid after the changes, too, as it will used as a pointer
where to insert the stored bits when performining undo.
*/
struct UndoElement
{
/// Which kind of operation are we recording for?
UndoKind kind;
/// the position of the cursor
StableDocIterator cursor;
/// the position of the cell described
StableDocIterator cell;
/// counted from begin of cell
pit_type from;
/// complement to end of this cell
pit_type end;
/// the contents of the saved Paragraphs (for texted)
ParagraphList * pars;
/// the contents of the saved MathData (for mathed)
MathData * array;
/// Only used in case of full backups
BufferParams bparams;
/// Only used in case of full backups
bool isFullBuffer;
};
struct Undo::Private
{
Private(Buffer & buffer): buffer_(buffer) {}
// Returns false if no undo possible.
bool textUndoOrRedo(DocIterator & cur, bool isUndoOperation);
///
void doRecordUndo(UndoKind kind,
DocIterator const & cell,
pit_type first_pit,
pit_type last_pit,
DocIterator const & cur,
bool isFullBuffer,
bool isUndoOperation);
///
void recordUndo(UndoKind kind,
DocIterator & cur,
pit_type first_pit,
pit_type last_pit);
///
Buffer & buffer_;
/// Undo stack.
limited_stack<UndoElement> undostack;
/// Redo stack.
limited_stack<UndoElement> redostack;
/// The flag used by Undo::finishUndo().
bool undo_finished;
};
Undo::Undo(Buffer & buffer): d(new Undo::Private(buffer))
{
}
Undo::~Undo()
{
delete d;
}
bool Undo::hasUndoStack() const
{
return !d->undostack.empty();
}
bool Undo::hasRedoStack() const
{
return !d->redostack.empty();
}
namespace { namespace {
/// The flag used by finishUndo(). std::ostream & operator<<(std::ostream & os, UndoElement const & undo)
bool undo_finished;
std::ostream & operator<<(std::ostream & os, Undo const & undo)
{ {
return os << " from: " << undo.from << " end: " << undo.end return os << " from: " << undo.from << " end: " << undo.end
<< " cell:\n" << undo.cell << " cell:\n" << undo.cell
@ -60,25 +158,26 @@ bool samePar(StableDocIterator const & i1, StableDocIterator const & i2)
return i1 == tmpi2; return i1 == tmpi2;
} }
} // namespace anon
void doRecordUndo(Undo::undo_kind kind,
void Undo::Private::doRecordUndo(UndoKind kind,
DocIterator const & cell, DocIterator const & cell,
pit_type first_pit, pit_type last_pit, pit_type first_pit, pit_type last_pit,
DocIterator const & cur, DocIterator const & cur,
BufferParams const & bparams,
bool isFullBuffer, bool isFullBuffer,
limited_stack<Undo> & stack) bool isUndoOperation)
{ {
if (first_pit > last_pit) if (first_pit > last_pit)
std::swap(first_pit, last_pit); std::swap(first_pit, last_pit);
// create the position information of the Undo entry // create the position information of the Undo entry
Undo undo; UndoElement undo;
undo.array = 0; undo.array = 0;
undo.pars = 0; undo.pars = 0;
undo.kind = kind; undo.kind = kind;
undo.cell = cell; undo.cell = cell;
undo.cursor = cur; undo.cursor = cur;
undo.bparams = bparams ; undo.bparams = buffer_.params();
undo.isFullBuffer = isFullBuffer; undo.isFullBuffer = isFullBuffer;
//lyxerr << "recordUndo: cur: " << cur << endl; //lyxerr << "recordUndo: cur: " << cur << endl;
//lyxerr << "recordUndo: pos: " << cur.pos() << endl; //lyxerr << "recordUndo: pos: " << cur.pos() << endl;
@ -86,11 +185,14 @@ void doRecordUndo(Undo::undo_kind kind,
undo.from = first_pit; undo.from = first_pit;
undo.end = cell.lastpit() - last_pit; undo.end = cell.lastpit() - last_pit;
limited_stack<UndoElement> & stack = isUndoOperation ?
undostack : redostack;
// Undo::ATOMIC are always recorded (no overlapping there). // Undo::ATOMIC are always recorded (no overlapping there).
// As nobody wants all removed character appear one by one when undoing, // As nobody wants all removed character appear one by one when undoing,
// we want combine 'similar' non-ATOMIC undo recordings to one. // we want combine 'similar' non-ATOMIC undo recordings to one.
if (!undo_finished if (!undo_finished
&& kind != Undo::ATOMIC && kind != ATOMIC_UNDO
&& !stack.empty() && !stack.empty()
&& samePar(stack.top().cell, undo.cell) && samePar(stack.top().cell, undo.cell)
&& stack.top().kind == undo.kind && stack.top().kind == undo.kind
@ -125,53 +227,58 @@ void doRecordUndo(Undo::undo_kind kind,
} }
void recordUndo(Undo::undo_kind kind, void Undo::Private::recordUndo(UndoKind kind, DocIterator & cur,
Cursor & cur, pit_type first_pit, pit_type last_pit, pit_type first_pit, pit_type last_pit)
limited_stack<Undo> & stack)
{ {
BOOST_ASSERT(first_pit <= cur.lastpit()); BOOST_ASSERT(first_pit <= cur.lastpit());
BOOST_ASSERT(last_pit <= cur.lastpit()); BOOST_ASSERT(last_pit <= cur.lastpit());
doRecordUndo(kind, cur, first_pit, last_pit, cur, doRecordUndo(kind, cur, first_pit, last_pit, cur,
cur.bv().buffer().params(), false, stack); false, true);
undo_finished = false;
redostack.clear();
//lyxerr << "undostack:\n";
//for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
// lyxerr << " " << i << ": " << buf.undostack()[i] << std::endl;
} }
bool Undo::Private::textUndoOrRedo(DocIterator & cur, bool isUndoOperation)
// Returns false if no undo possible.
bool textUndoOrRedo(BufferView & bv,
limited_stack<Undo> & stack, limited_stack<Undo> & otherstack)
{ {
finishUndo(); undo_finished = true;
if (stack.empty()) { limited_stack<UndoElement> & stack = isUndoOperation ?
undostack : redostack;
if (stack.empty())
// Nothing to do. // Nothing to do.
return false; return false;
}
limited_stack<UndoElement> & otherstack = isUndoOperation ?
redostack : undostack;
// Adjust undo stack and get hold of current undo data. // Adjust undo stack and get hold of current undo data.
Undo undo = stack.top(); UndoElement undo = stack.top();
stack.pop(); stack.pop();
// We will store in otherstack the part of the document under 'undo' // We will store in otherstack the part of the document under 'undo'
Buffer & buf = bv.buffer(); DocIterator cell_dit = undo.cell.asDocIterator(&buffer_.inset());
DocIterator cell_dit = undo.cell.asDocIterator(&buf.inset());
doRecordUndo(Undo::ATOMIC, cell_dit, doRecordUndo(ATOMIC_UNDO, cell_dit,
undo.from, cell_dit.lastpit() - undo.end, bv.cursor(), undo.from, cell_dit.lastpit() - undo.end, cur,
undo.bparams, undo.isFullBuffer, undo.isFullBuffer, !isUndoOperation);
otherstack);
// This does the actual undo/redo. // This does the actual undo/redo.
//lyxerr << "undo, performing: " << undo << std::endl; //lyxerr << "undo, performing: " << undo << std::endl;
bool labelsUpdateNeeded = false; bool labelsUpdateNeeded = false;
DocIterator dit = undo.cell.asDocIterator(&buf.inset()); DocIterator dit = undo.cell.asDocIterator(&buffer_.inset());
if (undo.isFullBuffer) { if (undo.isFullBuffer) {
BOOST_ASSERT(undo.pars); BOOST_ASSERT(undo.pars);
// This is a full document // This is a full document
otherstack.top().bparams = buf.params(); otherstack.top().bparams = buffer_.params();
buf.params() = undo.bparams; buffer_.params() = undo.bparams;
std::swap(buf.paragraphs(), *undo.pars); std::swap(buffer_.paragraphs(), *undo.pars);
delete undo.pars; delete undo.pars;
undo.pars = 0; undo.pars = 0;
} else if (dit.inMathed()) { } else if (dit.inMathed()) {
@ -215,103 +322,71 @@ bool textUndoOrRedo(BufferView & bv,
BOOST_ASSERT(undo.pars == 0); BOOST_ASSERT(undo.pars == 0);
BOOST_ASSERT(undo.array == 0); BOOST_ASSERT(undo.array == 0);
// Set cursor cur = undo.cursor.asDocIterator(&buffer_.inset());
Cursor & cur = bv.cursor();
cur.setCursor(undo.cursor.asDocIterator(&buf.inset()));
cur.selection() = false;
cur.resetAnchor();
cur.fixIfBroken();
if (labelsUpdateNeeded) if (labelsUpdateNeeded)
updateLabels(buf); updateLabels(buffer_);
finishUndo(); undo_finished = true;
return true; return true;
} }
} // namespace anon
void Undo::finishUndo()
void finishUndo()
{ {
// Make sure the next operation will be stored. // Make sure the next operation will be stored.
undo_finished = true; d->undo_finished = true;
} }
bool textUndo(BufferView & bv) bool Undo::textUndo(DocIterator & cur)
{ {
return textUndoOrRedo(bv, bv.buffer().undostack(), return d->textUndoOrRedo(cur, true);
bv.buffer().redostack());
} }
bool textRedo(BufferView & bv) bool Undo::textRedo(DocIterator & cur)
{ {
return textUndoOrRedo(bv, bv.buffer().redostack(), return d->textUndoOrRedo(cur, false);
bv.buffer().undostack());
} }
void recordUndo(Undo::undo_kind kind, void Undo::recordUndo(DocIterator & cur, UndoKind kind)
Cursor & cur, pit_type first, pit_type last)
{ {
Buffer & buf = cur.bv().buffer(); d->recordUndo(kind, cur, cur.pit(), cur.pit());
recordUndo(kind, cur, first, last, buf.undostack());
buf.redostack().clear();
//lyxerr << "undostack:\n";
//for (size_t i = 0, n = buf.undostack().size(); i != n && i < 6; ++i)
// lyxerr << " " << i << ": " << buf.undostack()[i] << std::endl;
} }
void recordUndo(Cursor & cur, Undo::undo_kind kind) void Undo::recordUndoInset(DocIterator & cur, UndoKind kind)
{ {
recordUndo(kind, cur, cur.pit(), cur.pit()); DocIterator c = cur;
c.pop_back();
d->doRecordUndo(kind, c, c.pit(), c.pit(), cur, false, true);
} }
void recordUndoInset(Cursor & cur, Undo::undo_kind kind) void Undo::recordUndo(DocIterator & cur, UndoKind kind, pit_type from)
{ {
Cursor c = cur; d->recordUndo(kind, cur, cur.pit(), from);
c.pop();
Buffer & buf = cur.bv().buffer();
doRecordUndo(kind, c, c.pit(), c.pit(), cur,
buf.params(), false, buf.undostack());
} }
void recordUndoSelection(Cursor & cur, Undo::undo_kind kind) void Undo::recordUndo(DocIterator & cur, UndoKind kind,
{
recordUndo(kind, cur, cur.selBegin().pit(), cur.selEnd().pit());
}
void recordUndo(Cursor & cur, Undo::undo_kind kind, pit_type from)
{
recordUndo(kind, cur, cur.pit(), from);
}
void recordUndo(Cursor & cur, Undo::undo_kind kind,
pit_type from, pit_type to) pit_type from, pit_type to)
{ {
recordUndo(kind, cur, from, to); d->recordUndo(kind, cur, from, to);
} }
void recordUndoFullDocument(BufferView * bv) void Undo::recordUndoFullDocument(DocIterator & cur)
{ {
Buffer & buf = bv->buffer(); d->doRecordUndo(
doRecordUndo( ATOMIC_UNDO,
Undo::ATOMIC, doc_iterator_begin(d->buffer_.inset()),
doc_iterator_begin(buf.inset()), 0, d->buffer_.paragraphs().size() - 1,
0, buf.paragraphs().size() - 1, cur,
bv->cursor(),
buf.params(),
true, true,
buf.undostack() true
); );
undo_finished = false;
} }

View File

@ -9,6 +9,7 @@
* \author John Levon * \author John Levon
* \author André Pönitz * \author André Pönitz
* \author Jürgen Vigna * \author Jürgen Vigna
* \author Abdelrazak Younes
* *
* Full author contact details are available in file CREDITS. * Full author contact details are available in file CREDITS.
*/ */
@ -16,95 +17,33 @@
#ifndef UNDO_H #ifndef UNDO_H
#define UNDO_H #define UNDO_H
#include "DocIterator.h"
#include "BufferParams.h"
#include "support/types.h" #include "support/types.h"
#include <string>
namespace lyx { namespace lyx {
class Buffer;
class BufferParams; class BufferParams;
class BufferView;
class DocIterator; class DocIterator;
class Cursor;
class MathData; class MathData;
class ParagraphList; class ParagraphList;
/// This is used to combine consecutive undo recordings of the same kind.
/** enum UndoKind {
These are the elements put on the undo stack. Each object contains complete /**
paragraphs from some cell and sufficient information to restore the cursor * Insert something - these will combine to one big chunk
state. * when many inserts come after each other.
*/
The cell is given by a DocIterator pointing to this cell, the 'interesting' INSERT_UNDO,
range of paragraphs by counting them from begin and end of cell, /**
respectively. * Delete something - these will combine to one big chunk
* when many deletes come after each other.
The cursor is also given as DocIterator and should point to some place in */
the stored paragraph range. In case of math, we simply store the whole DELETE_UNDO,
cell, as there usually is just a simple paragraph in a cell. /// Atomic - each of these will have its own entry in the stack
ATOMIC_UNDO
The idea is to store the contents of 'interesting' paragraphs in some
structure ('Undo') _before_ it is changed in some edit operation.
Obviously, the stored ranged should be as small as possible. However, it
there is a lower limit: The StableDocIterator pointing stored in the undo
class must be valid after the changes, too, as it will used as a pointer
where to insert the stored bits when performining undo.
*/
class Undo {
public:
/// This is used to combine consecutive undo recordings of the same kind.
enum undo_kind {
/**
* Insert something - these will combine to one big chunk
* when many inserts come after each other.
*/
INSERT,
/**
* Delete something - these will combine to one big chunk
* when many deletes come after each other.
*/
DELETE,
/// Atomic - each of these will have its own entry in the stack
ATOMIC
};
/// Which kind of operation are we recording for?
undo_kind kind;
/// the position of the cursor
StableDocIterator cursor;
/// the position of the cell described
StableDocIterator cell;
/// counted from begin of cell
pit_type from;
/// complement to end of this cell
pit_type end;
/// the contents of the saved Paragraphs (for texted)
ParagraphList * pars;
/// the contents of the saved MathData (for mathed)
MathData * array;
/// Only used in case of full backups
BufferParams bparams;
/// Only used in case of full backups
bool isFullBuffer;
}; };
/// this will undo the last action - returns false if no undo possible
bool textUndo(BufferView & bv);
/// this will redo the last undo - returns false if no redo possible
bool textRedo(BufferView & bv);
/// makes sure the next operation will be stored
void finishUndo();
/** /**
* Record undo information - call with the current cursor and the 'other * Record undo information - call with the current cursor and the 'other
* end' of the range of changed paragraphs. So we give an inclusive range. * end' of the range of changed paragraphs. So we give an inclusive range.
@ -115,31 +54,54 @@ void finishUndo();
* Right now we use recordUndoInset if more than one cell is changed, * Right now we use recordUndoInset if more than one cell is changed,
* but that puts the cursor in front of the inset after undo. We would need * but that puts the cursor in front of the inset after undo. We would need
* something like * something like
* recordUndoGrid(Cursor & cur, Undo::undo_kind kind, idx_type from, idx_type to); * recordUndoGrid(DocIterator & cur, UndoKind kind, idx_type from, idx_type to);
* and store the cell information in class Undo. * and store the cell information in class Undo.
*/ */
class Undo
{
public:
/// The general case: prepare undo for an arbitrary range. Undo(Buffer &);
/// FIXME: replace Cursor with DocIterator. This is not possible right
/// now because we need access to Buffer->params()!.
void recordUndo(Cursor & cur, Undo::undo_kind kind,
pit_type from, pit_type to);
/// Convenience: prepare undo for the range between 'from' and cursor. ~Undo();
void recordUndo(Cursor & cur, Undo::undo_kind kind, pit_type from);
/// Convenience: prepare undo for the single paragraph or cell /// this will undo the last action - returns false if no undo possible
/// containing the cursor bool textUndo(DocIterator &);
void recordUndo(Cursor & cur, Undo::undo_kind kind = Undo::ATOMIC);
/// Convenience: prepare undo for the inset containing the cursor /// this will redo the last undo - returns false if no redo possible
void recordUndoInset(Cursor & cur, Undo::undo_kind kind = Undo::ATOMIC); bool textRedo(DocIterator &);
/// Convenience: prepare undo for the selected paragraphs
void recordUndoSelection(Cursor & cur, Undo::undo_kind kind = Undo::ATOMIC); /// makes sure the next operation will be stored
void finishUndo();
///
bool hasUndoStack() const;
///
bool hasRedoStack() const;
/// The general case: prepare undo for an arbitrary range.
void recordUndo(DocIterator & cur, UndoKind kind,
pit_type from, pit_type to);
/// Convenience: prepare undo for the range between 'from' and cursor.
void recordUndo(DocIterator & cur, UndoKind kind, pit_type from);
/// Convenience: prepare undo for the single paragraph or cell
/// containing the cursor
void recordUndo(DocIterator & cur, UndoKind kind = ATOMIC_UNDO);
/// Convenience: prepare undo for the inset containing the cursor
void recordUndoInset(DocIterator & cur, UndoKind kind = ATOMIC_UNDO);
/// Convenience: prepare undo for the whole buffer
void recordUndoFullDocument(DocIterator & cur);
private:
struct Private;
Private * const d;
};
/// Convenience: prepare undo for the whole buffer
void recordUndoFullDocument(BufferView * bv);
} // namespace lyx } // namespace lyx
#endif // UNDO_FUNCS_H #endif // UNDO_H

View File

@ -44,7 +44,6 @@
#include "ParagraphParameters.h" #include "ParagraphParameters.h"
#include "ParIterator.h" #include "ParIterator.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "Undo.h"
#include "support/convert.h" #include "support/convert.h"
#include "support/lstrings.h" #include "support/lstrings.h"
@ -3154,7 +3153,7 @@ docstring const InsetTabular::editMessage() const
void InsetTabular::edit(Cursor & cur, bool left) void InsetTabular::edit(Cursor & cur, bool left)
{ {
//lyxerr << "InsetTabular::edit: " << this << endl; //lyxerr << "InsetTabular::edit: " << this << endl;
finishUndo(); cur.finishUndo();
cur.selection() = false; cur.selection() = false;
cur.push(*this); cur.push(*this);
if (left) { if (left) {
@ -3384,7 +3383,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_CUT: case LFUN_CUT:
if (tablemode(cur)) { if (tablemode(cur)) {
if (copySelection(cur)) { if (copySelection(cur)) {
recordUndoInset(cur, Undo::DELETE); cur.recordUndoInset(DELETE_UNDO);
cutSelection(cur); cutSelection(cur);
} }
} }
@ -3395,7 +3394,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_CHAR_DELETE_BACKWARD: case LFUN_CHAR_DELETE_BACKWARD:
case LFUN_CHAR_DELETE_FORWARD: case LFUN_CHAR_DELETE_FORWARD:
if (tablemode(cur)) { if (tablemode(cur)) {
recordUndoInset(cur, Undo::DELETE); cur.recordUndoInset(DELETE_UNDO);
cutSelection(cur); cutSelection(cur);
} }
else else
@ -3406,7 +3405,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
if (!cur.selection()) if (!cur.selection())
break; break;
if (tablemode(cur)) { if (tablemode(cur)) {
finishUndo(); cur.finishUndo();
copySelection(cur); copySelection(cur);
} else } else
cell(cur.idx())->dispatch(cur, cmd); cell(cur.idx())->dispatch(cur, cmd);
@ -3437,7 +3436,7 @@ void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_PASTE: case LFUN_PASTE:
if (tabularStackDirty() && theClipboard().isInternal()) { if (tabularStackDirty() && theClipboard().isInternal()) {
recordUndoInset(cur, Undo::INSERT); cur.recordUndoInset(INSERT_UNDO);
pasteClipboard(cur); pasteClipboard(cur);
break; break;
} }
@ -4127,7 +4126,7 @@ void InsetTabular::tabularFeatures(Cursor & cur,
break; break;
} }
recordUndoInset(cur, Undo::ATOMIC); cur.recordUndoInset(ATOMIC_UNDO);
getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end); getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
row_type const row = tabular.cellRow(cur.idx()); row_type const row = tabular.cellRow(cur.idx());

View File

@ -44,7 +44,6 @@
#include "sgml.h" #include "sgml.h"
#include "TextMetrics.h" #include "TextMetrics.h"
#include "TexRow.h" #include "TexRow.h"
#include "Undo.h"
#include "frontends/alert.h" #include "frontends/alert.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
@ -217,7 +216,7 @@ void InsetText::edit(Cursor & cur, bool left)
int const pos = left ? 0 : paragraphs().back().size(); int const pos = left ? 0 : paragraphs().back().size();
text_.setCursor(cur.top(), pit, pos); text_.setCursor(cur.top(), pit, pos);
cur.clearSelection(); cur.clearSelection();
finishUndo(); cur.finishUndo();
} }

View File

@ -16,6 +16,7 @@
#include "lyxfind.h" #include "lyxfind.h"
#include "Buffer.h" #include "Buffer.h"
#include "BufferParams.h"
#include "Cursor.h" #include "Cursor.h"
#include "CutAndPaste.h" #include "CutAndPaste.h"
#include "buffer_funcs.h" #include "buffer_funcs.h"
@ -26,7 +27,6 @@
#include "Text.h" #include "Text.h"
#include "Paragraph.h" #include "Paragraph.h"
#include "ParIterator.h" #include "ParIterator.h"
#include "Undo.h"
#include "frontends/alert.h" #include "frontends/alert.h"
@ -177,7 +177,7 @@ int replaceAll(BufferView * bv,
if (!searchAllowed(bv, searchstr) || buf.isReadonly()) if (!searchAllowed(bv, searchstr) || buf.isReadonly())
return 0; return 0;
recordUndoFullDocument(bv); bv->cursor().recordUndoFullDocument();
MatchString const match(searchstr, cs, mw); MatchString const match(searchstr, cs, mw);
int num = 0; int num = 0;

View File

@ -20,7 +20,6 @@
#include "Cursor.h" #include "Cursor.h"
#include "FuncRequest.h" #include "FuncRequest.h"
#include "gettext.h" #include "gettext.h"
#include "Undo.h"
#include "support/lstrings.h" #include "support/lstrings.h"
@ -72,7 +71,7 @@ void InsetMathCases::doDispatch(Cursor & cur, FuncRequest & cmd)
//lyxerr << "*** InsetMathCases: request: " << cmd << endl; //lyxerr << "*** InsetMathCases: request: " << cmd << endl;
switch (cmd.action) { switch (cmd.action) {
case LFUN_TABULAR_FEATURE: { case LFUN_TABULAR_FEATURE: {
recordUndo(cur); cur.recordUndo();
docstring const & s = cmd.argument(); docstring const & s = cmd.argument();
if (s == "add-vline-left" || s == "add-vline-right") { if (s == "add-vline-left" || s == "add-vline-right") {
cur.undispatched(); cur.undispatched();

View File

@ -23,7 +23,6 @@
#include "debug.h" #include "debug.h"
#include "FuncRequest.h" #include "FuncRequest.h"
#include "gettext.h" #include "gettext.h"
#include "Undo.h"
#include "frontends/Clipboard.h" #include "frontends/Clipboard.h"
#include "frontends/Painter.h" #include "frontends/Painter.h"
@ -1081,7 +1080,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
// multiple cells. Unfortunately this puts the cursor in front // multiple cells. Unfortunately this puts the cursor in front
// of the inset after undo. This is (especilally for large // of the inset after undo. This is (especilally for large
// grids) annoying. // grids) annoying.
recordUndoInset(cur); cur.recordUndoInset();
//autocorrect_ = false; //autocorrect_ = false;
//macroModeClose(); //macroModeClose();
//if (selection_) { //if (selection_) {
@ -1097,7 +1096,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
break; break;
case LFUN_CELL_SPLIT: case LFUN_CELL_SPLIT:
recordUndo(cur); cur.recordUndo();
splitCell(cur); splitCell(cur);
break; break;
@ -1121,7 +1120,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
break; break;
case LFUN_BREAK_LINE: { case LFUN_BREAK_LINE: {
recordUndoInset(cur); cur.recordUndoInset();
row_type const r = cur.row(); row_type const r = cur.row();
addRow(r); addRow(r);
@ -1142,7 +1141,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
} }
case LFUN_TABULAR_FEATURE: { case LFUN_TABULAR_FEATURE: {
recordUndoInset(cur); cur.recordUndoInset();
//lyxerr << "handling tabular-feature " << to_utf8(cmd.argument()) << endl; //lyxerr << "handling tabular-feature " << to_utf8(cmd.argument()) << endl;
istringstream is(to_utf8(cmd.argument())); istringstream is(to_utf8(cmd.argument()));
string s; string s;
@ -1272,12 +1271,12 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
if (grid.nargs() == 1) { if (grid.nargs() == 1) {
// single cell/part of cell // single cell/part of cell
recordUndo(cur); cur.recordUndo();
cur.cell().insert(cur.pos(), grid.cell(0)); cur.cell().insert(cur.pos(), grid.cell(0));
cur.pos() += grid.cell(0).size(); cur.pos() += grid.cell(0).size();
} else { } else {
// multiple cells // multiple cells
recordUndoInset(cur); cur.recordUndoInset();
col_type const numcols = col_type const numcols =
min(grid.ncols(), ncols() - col(cur.idx())); min(grid.ncols(), ncols() - col(cur.idx()));
row_type const numrows = row_type const numrows =
@ -1299,7 +1298,7 @@ void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
cell(i).append(grid.cell(grid.index(r, c))); cell(i).append(grid.cell(grid.index(r, c)));
} }
cur.clearSelection(); // bug 393 cur.clearSelection(); // bug 393
finishUndo(); cur.finishUndo();
break; break;
} }

View File

@ -42,7 +42,6 @@
#include "sgml.h" #include "sgml.h"
#include "Text.h" #include "Text.h"
#include "TextPainter.h" #include "TextPainter.h"
#include "Undo.h"
#include "insets/RenderPreview.h" #include "insets/RenderPreview.h"
#include "insets/InsetLabel.h" #include "insets/InsetLabel.h"
@ -1052,7 +1051,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_BREAK_LINE: case LFUN_BREAK_LINE:
// some magic for the common case // some magic for the common case
if (type_ == hullSimple || type_ == hullEquation) { if (type_ == hullSimple || type_ == hullEquation) {
recordUndoInset(cur); cur.recordUndoInset();
bool const align = bool const align =
cur.bv().buffer().params().use_amsmath == BufferParams::package_on; cur.bv().buffer().params().use_amsmath == BufferParams::package_on;
mutate(align ? hullAlign : hullEqnArray); mutate(align ? hullAlign : hullEqnArray);
@ -1065,7 +1064,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_MATH_NUMBER: case LFUN_MATH_NUMBER:
//lyxerr << "toggling all numbers" << endl; //lyxerr << "toggling all numbers" << endl;
if (display()) { if (display()) {
recordUndoInset(cur); cur.recordUndoInset();
bool old = numberedType(); bool old = numberedType();
if (type_ == hullMultline) if (type_ == hullMultline)
numbered(nrows() - 1, !old); numbered(nrows() - 1, !old);
@ -1079,7 +1078,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
case LFUN_MATH_NONUMBER: case LFUN_MATH_NONUMBER:
if (display()) { if (display()) {
recordUndoInset(cur); cur.recordUndoInset();
row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row(); row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
bool old = numbered(r); bool old = numbered(r);
cur.message(old ? _("No number") : _("Number")); cur.message(old ? _("No number") : _("Number"));
@ -1088,7 +1087,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
break; break;
case LFUN_LABEL_INSERT: { case LFUN_LABEL_INSERT: {
recordUndoInset(cur); cur.recordUndoInset();
row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row(); row_type r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
docstring old_label = label(r); docstring old_label = label(r);
docstring const default_label = from_ascii( docstring const default_label = from_ascii(
@ -1116,7 +1115,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
InsetCommandParams p("label"); InsetCommandParams p("label");
InsetCommandMailer::string2params(name, to_utf8(cmd.argument()), p); InsetCommandMailer::string2params(name, to_utf8(cmd.argument()), p);
docstring str = p["name"]; docstring str = p["name"];
recordUndoInset(cur); cur.recordUndoInset();
row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row(); row_type const r = (type_ == hullMultline) ? nrows() - 1 : cur.row();
str = support::trim(str); str = support::trim(str);
if (!str.empty()) if (!str.empty())
@ -1134,12 +1133,12 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
} }
case LFUN_MATH_EXTERN: case LFUN_MATH_EXTERN:
recordUndoInset(cur); cur.recordUndoInset();
doExtern(cur, cmd); doExtern(cur, cmd);
break; break;
case LFUN_MATH_MUTATE: { case LFUN_MATH_MUTATE: {
recordUndoInset(cur); cur.recordUndoInset();
row_type row = cur.row(); row_type row = cur.row();
col_type col = cur.col(); col_type col = cur.col();
mutate(hullType(cmd.argument())); mutate(hullType(cmd.argument()));
@ -1155,7 +1154,7 @@ void InsetMathHull::doDispatch(Cursor & cur, FuncRequest & cmd)
} }
case LFUN_MATH_DISPLAY: { case LFUN_MATH_DISPLAY: {
recordUndoInset(cur); cur.recordUndoInset();
mutate(type_ == hullSimple ? hullEquation : hullSimple); mutate(type_ == hullSimple ? hullEquation : hullSimple);
cur.idx() = 0; cur.idx() = 0;
cur.pos() = cur.lastpos(); cur.pos() = cur.lastpos();
@ -1287,7 +1286,7 @@ void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
{ {
// this whole function is a hack and won't work for incremental font // this whole function is a hack and won't work for incremental font
// changes... // changes...
recordUndo(cur); cur.recordUndo();
if (cur.inset().asInsetMath()->name() == font) if (cur.inset().asInsetMath()->name() == font)
cur.handleFont(to_utf8(font)); cur.handleFont(to_utf8(font));
else { else {
@ -1299,7 +1298,7 @@ void InsetMathHull::handleFont(Cursor & cur, docstring const & arg,
void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg) void InsetMathHull::handleFont2(Cursor & cur, docstring const & arg)
{ {
recordUndo(cur); cur.recordUndo();
Font font; Font font;
bool b; bool b;
font.fromString(to_utf8(arg), b); font.fromString(to_utf8(arg), b);

View File

@ -46,7 +46,6 @@
#include "gettext.h" #include "gettext.h"
#include "Text.h" #include "Text.h"
#include "OutputParams.h" #include "OutputParams.h"
#include "Undo.h"
#include "support/lstrings.h" #include "support/lstrings.h"
#include "support/textutils.h" #include "support/textutils.h"
@ -416,10 +415,10 @@ void InsetMathNest::handleFont
// changes... // changes...
if (cur.inset().asInsetMath()->name() == font) { if (cur.inset().asInsetMath()->name() == font) {
recordUndoInset(cur, Undo::ATOMIC); cur.recordUndoInset();
cur.handleFont(to_utf8(font)); cur.handleFont(to_utf8(font));
} else { } else {
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
cur.handleNest(createInsetMath(font)); cur.handleNest(createInsetMath(font));
cur.insert(arg); cur.insert(arg);
} }
@ -428,7 +427,7 @@ void InsetMathNest::handleFont
void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg) void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
{ {
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
Font font; Font font;
bool b; bool b;
font.fromString(to_utf8(arg), b); font.fromString(to_utf8(arg), b);
@ -447,7 +446,7 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
switch (cmd.action) { switch (cmd.action) {
case LFUN_PASTE: { case LFUN_PASTE: {
recordUndo(cur); cur.recordUndo();
cur.message(_("Paste")); cur.message(_("Paste"));
replaceSelection(cur); replaceSelection(cur);
docstring topaste; docstring topaste;
@ -461,12 +460,12 @@ void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
} }
cur.niceInsert(topaste); cur.niceInsert(topaste);
cur.clearSelection(); // bug 393 cur.clearSelection(); // bug 393
finishUndo(); cur.finishUndo();
break; break;
} }
case LFUN_CUT: case LFUN_CUT:
recordUndo(cur); cur.recordUndo();
cutSelection(cur, true, true); cutSelection(cur, true, true);
cur.message(_("Cut")); cur.message(_("Cut"));
// Prevent stale position >= size crash // Prevent stale position >= size crash
@ -670,9 +669,9 @@ goto_char_backwards:
case LFUN_CHAR_DELETE_BACKWARD: case LFUN_CHAR_DELETE_BACKWARD:
if (cur.pos() == 0) if (cur.pos() == 0)
// May affect external cell: // May affect external cell:
recordUndoInset(cur, Undo::ATOMIC); cur.recordUndoInset();
else else
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
// if the inset can not be removed from within, delete it // if the inset can not be removed from within, delete it
if (!cur.backspace()) { if (!cur.backspace()) {
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD); FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
@ -684,9 +683,9 @@ goto_char_backwards:
case LFUN_CHAR_DELETE_FORWARD: case LFUN_CHAR_DELETE_FORWARD:
if (cur.pos() == cur.lastpos()) if (cur.pos() == cur.lastpos())
// May affect external cell: // May affect external cell:
recordUndoInset(cur, Undo::ATOMIC); cur.recordUndoInset();
else else
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
// if the inset can not be removed from within, delete it // if the inset can not be removed from within, delete it
if (!cur.erase()) { if (!cur.erase()) {
FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD); FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
@ -704,14 +703,14 @@ goto_char_backwards:
break; break;
case LFUN_INSET_TOGGLE: case LFUN_INSET_TOGGLE:
recordUndo(cur); cur.recordUndo();
lock(!lock()); lock(!lock());
cur.popRight(); cur.popRight();
break; break;
case LFUN_SELF_INSERT: case LFUN_SELF_INSERT:
if (cmd.argument().size() != 1) { if (cmd.argument().size() != 1) {
recordUndo(cur); cur.recordUndo();
docstring const arg = cmd.argument(); docstring const arg = cmd.argument();
if (!interpretString(cur, arg)) if (!interpretString(cur, arg))
cur.insert(arg); cur.insert(arg);
@ -727,7 +726,7 @@ goto_char_backwards:
// A side effect is that an undo before the macro is finished // A side effect is that an undo before the macro is finished
// undoes the complete macro, not only the last character. // undoes the complete macro, not only the last character.
if (!cur.inMacroMode()) if (!cur.inMacroMode())
recordUndo(cur); cur.recordUndo();
// spacial handling of space. If we insert an inset // spacial handling of space. If we insert an inset
// via macro mode, we want to put the cursor inside it // via macro mode, we want to put the cursor inside it
@ -766,7 +765,7 @@ goto_char_backwards:
if (cmd.argument().empty()) { if (cmd.argument().empty()) {
// do superscript if LyX handles // do superscript if LyX handles
// deadkeys // deadkeys
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
script(cur, true, grabAndEraseSelection(cur)); script(cur, true, grabAndEraseSelection(cur));
} }
break; break;
@ -875,13 +874,13 @@ goto_char_backwards:
case LFUN_MATH_SIZE: case LFUN_MATH_SIZE:
#if 0 #if 0
recordUndo(cur); cur.recordUndo();
cur.setSize(arg); cur.setSize(arg);
#endif #endif
break; break;
case LFUN_MATH_MATRIX: { case LFUN_MATH_MATRIX: {
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
unsigned int m = 1; unsigned int m = 1;
unsigned int n = 1; unsigned int n = 1;
docstring v_align; docstring v_align;
@ -906,7 +905,7 @@ goto_char_backwards:
ls = '('; ls = '(';
if (rs.empty()) if (rs.empty())
rs = ')'; rs = ')';
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
cur.handleNest(MathAtom(new InsetMathDelim(ls, rs))); cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
break; break;
} }
@ -925,7 +924,7 @@ goto_char_backwards:
// We mimic LFUN_MATH_DELIM in case we have an empty left // We mimic LFUN_MATH_DELIM in case we have an empty left
// or right delimiter. // or right delimiter.
if (have_l || have_r) { if (have_l || have_r) {
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
docstring const selection = grabAndEraseSelection(cur); docstring const selection = grabAndEraseSelection(cur);
selClearOrDel(cur); selClearOrDel(cur);
if (have_l) if (have_l)
@ -943,31 +942,31 @@ goto_char_backwards:
case LFUN_SPACE_INSERT: case LFUN_SPACE_INSERT:
case LFUN_MATH_SPACE: case LFUN_MATH_SPACE:
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
cur.insert(MathAtom(new InsetMathSpace(from_ascii(",")))); cur.insert(MathAtom(new InsetMathSpace(from_ascii(","))));
break; break;
case LFUN_ERT_INSERT: case LFUN_ERT_INSERT:
// interpret this as if a backslash was typed // interpret this as if a backslash was typed
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
interpretChar(cur, '\\'); interpretChar(cur, '\\');
break; break;
case LFUN_MATH_SUBSCRIPT: case LFUN_MATH_SUBSCRIPT:
// interpret this as if a _ was typed // interpret this as if a _ was typed
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
interpretChar(cur, '_'); interpretChar(cur, '_');
break; break;
case LFUN_MATH_SUPERSCRIPT: case LFUN_MATH_SUPERSCRIPT:
// interpret this as if a ^ was typed // interpret this as if a ^ was typed
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
interpretChar(cur, '^'); interpretChar(cur, '^');
break; break;
case LFUN_QUOTE_INSERT: case LFUN_QUOTE_INSERT:
// interpret this as if a straight " was typed // interpret this as if a straight " was typed
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
interpretChar(cur, '\"'); interpretChar(cur, '\"');
break; break;
@ -975,7 +974,7 @@ goto_char_backwards:
// handling such that "self-insert" works on "arbitrary stuff" too, and // handling such that "self-insert" works on "arbitrary stuff" too, and
// math-insert only handles special math things like "matrix". // math-insert only handles special math things like "matrix".
case LFUN_MATH_INSERT: { case LFUN_MATH_INSERT: {
recordUndo(cur, Undo::ATOMIC); cur.recordUndo();
if (cmd.argument() == "^" || cmd.argument() == "_") { if (cmd.argument() == "^" || cmd.argument() == "_") {
interpretChar(cur, cmd.argument()[0]); interpretChar(cur, cmd.argument()[0]);
} else } else
@ -997,7 +996,7 @@ goto_char_backwards:
case LFUN_INSET_INSERT: { case LFUN_INSET_INSERT: {
MathData ar; MathData ar;
if (createInsetMath_fromDialogStr(cmd.argument(), ar)) { if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
recordUndo(cur); cur.recordUndo();
cur.insert(ar); cur.insert(ar);
} else } else
cur.undispatched(); cur.undispatched();
@ -1005,7 +1004,7 @@ goto_char_backwards:
} }
case LFUN_INSET_DISSOLVE: case LFUN_INSET_DISSOLVE:
if (!asHullInset()) { if (!asHullInset()) {
recordUndoInset(cur, Undo::ATOMIC); cur.recordUndoInset();
cur.pullArg(); cur.pullArg();
} }
break; break;

View File

@ -20,7 +20,6 @@
#include "Cursor.h" #include "Cursor.h"
#include "debug.h" #include "debug.h"
#include "FuncRequest.h" #include "FuncRequest.h"
#include "Undo.h"
#include <boost/assert.hpp> #include <boost/assert.hpp>
@ -673,18 +672,18 @@ bool InsetMathScript::notifyCursorLeaves(Cursor & cur)
// Case of two scripts. In this case, 1 = super, 2 = sub // Case of two scripts. In this case, 1 = super, 2 = sub
if (cur.idx() == 2 && cell(2).empty()) { if (cur.idx() == 2 && cell(2).empty()) {
// must be a subscript... // must be a subscript...
recordUndoInset(cur); cur.recordUndoInset();
removeScript(false); removeScript(false);
return true; return true;
} else if (cur.idx() == 1 && cell(1).empty()) { } else if (cur.idx() == 1 && cell(1).empty()) {
// must be a superscript... // must be a superscript...
recordUndoInset(cur); cur.recordUndoInset();
removeScript(true); removeScript(true);
return true; return true;
} }
} else if (nargs() > 1 && cur.idx() == 1 && cell(1).empty()) { } else if (nargs() > 1 && cur.idx() == 1 && cell(1).empty()) {
// could be either subscript or super script // could be either subscript or super script
recordUndoInset(cur); cur.recordUndoInset();
removeScript(cell_1_is_up_); removeScript(cell_1_is_up_);
// Let the script inset commit suicide. This is // Let the script inset commit suicide. This is
// modelled on Cursor.pullArg(), but tries not to // modelled on Cursor.pullArg(), but tries not to

View File

@ -13,6 +13,7 @@
#define FILENAME_H #define FILENAME_H
#include <string> #include <string>
#include <ctime>
namespace lyx { namespace lyx {