sw: allow undo of typing in 2 views independent from each other
Undoing out of order is dangerous by default, so limit this to a very specific case as a start, that allows growing in follow-up commits. For now, allow out of order undo if: 1) redo stack is empty 2) we're in LOK mode (different views represent different users) 3) we undo a single action (count is 1) 4) the top undo action doesn't belong to the current view 5) the top and the previous undo actions are independent Which only requires that SwUndoInsert::UndoImpl() is independent for two different paragraphs, which seems to be the case. Independent undo actions opt in for this, currently the only such allowed undo action is SwUndoInsert ("typing"), which adds characters to a single text node. Even those are only considered independent if they operate on different text nodes. On the positive side, this allows out of order undo in the frequent case where two users collaborate on a long document and they just type some new content into the document at different paragraphs. Change-Id: Ibb4551e8f7046b4947491b8bf751eaa0cbb2d060 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/124949 Reviewed-by: Miklos Vajna <vmiklos@collabora.com> Tested-by: Jenkins
This commit is contained in:
@@ -42,6 +42,12 @@ public:
|
|||||||
class SVL_DLLPUBLIC SfxUndoContext
|
class SVL_DLLPUBLIC SfxUndoContext
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Don't undo the top undo action, but an earlier one. It's the caller's responsibility to
|
||||||
|
* ensure that the earlier undo action is independent from the following ones.
|
||||||
|
*/
|
||||||
|
virtual size_t GetUndoOffset() { return 0; }
|
||||||
|
|
||||||
virtual ~SfxUndoContext() = 0;
|
virtual ~SfxUndoContext() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -683,6 +683,16 @@ bool SfxUndoManager::ImplUndo( SfxUndoContext* i_contextOrNull )
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i_contextOrNull && i_contextOrNull->GetUndoOffset() == 1)
|
||||||
|
{
|
||||||
|
if (m_xData->pActUndoArray->nCurUndoAction >= 2)
|
||||||
|
{
|
||||||
|
std::swap(
|
||||||
|
m_xData->pActUndoArray->maUndoActions[m_xData->pActUndoArray->nCurUndoAction - 1],
|
||||||
|
m_xData->pActUndoArray->maUndoActions[m_xData->pActUndoArray->nCurUndoAction - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ --m_xData->pActUndoArray->nCurUndoAction ].pAction.get();
|
SfxUndoAction* pAction = m_xData->pActUndoArray->maUndoActions[ --m_xData->pActUndoArray->nCurUndoAction ].pAction.get();
|
||||||
const OUString sActionComment = pAction->GetComment();
|
const OUString sActionComment = pAction->GetComment();
|
||||||
try
|
try
|
||||||
|
@@ -212,6 +212,9 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual void SetView(SwView* pView) = 0;
|
virtual void SetView(SwView* pView) = 0;
|
||||||
|
|
||||||
|
/// Zero offset means undoing the top undo action.
|
||||||
|
virtual bool UndoWithOffset(size_t nUndoOffset) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual ~IDocumentUndoRedo() {};
|
virtual ~IDocumentUndoRedo() {};
|
||||||
};
|
};
|
||||||
|
@@ -602,7 +602,7 @@ public:
|
|||||||
/// should only be called by sw::UndoManager!
|
/// should only be called by sw::UndoManager!
|
||||||
void HandleUndoRedoContext(::sw::UndoRedoContext & rContext);
|
void HandleUndoRedoContext(::sw::UndoRedoContext & rContext);
|
||||||
|
|
||||||
void Undo(sal_uInt16 const nCount = 1);
|
void Undo(sal_uInt16 const nCount = 1, sal_uInt16 nOffset = 0);
|
||||||
void Redo(sal_uInt16 const nCount = 1);
|
void Redo(sal_uInt16 const nCount = 1);
|
||||||
void Repeat(sal_uInt16 const nCount);
|
void Repeat(sal_uInt16 const nCount);
|
||||||
|
|
||||||
|
@@ -108,6 +108,7 @@ public:
|
|||||||
void testTextEditViewInvalidations();
|
void testTextEditViewInvalidations();
|
||||||
void testUndoInvalidations();
|
void testUndoInvalidations();
|
||||||
void testUndoLimiting();
|
void testUndoLimiting();
|
||||||
|
void testUndoReordering();
|
||||||
void testUndoShapeLimiting();
|
void testUndoShapeLimiting();
|
||||||
void testUndoDispatch();
|
void testUndoDispatch();
|
||||||
void testUndoRepairDispatch();
|
void testUndoRepairDispatch();
|
||||||
@@ -189,6 +190,7 @@ public:
|
|||||||
CPPUNIT_TEST(testTextEditViewInvalidations);
|
CPPUNIT_TEST(testTextEditViewInvalidations);
|
||||||
CPPUNIT_TEST(testUndoInvalidations);
|
CPPUNIT_TEST(testUndoInvalidations);
|
||||||
CPPUNIT_TEST(testUndoLimiting);
|
CPPUNIT_TEST(testUndoLimiting);
|
||||||
|
CPPUNIT_TEST(testUndoReordering);
|
||||||
CPPUNIT_TEST(testUndoShapeLimiting);
|
CPPUNIT_TEST(testUndoShapeLimiting);
|
||||||
CPPUNIT_TEST(testUndoDispatch);
|
CPPUNIT_TEST(testUndoDispatch);
|
||||||
CPPUNIT_TEST(testUndoRepairDispatch);
|
CPPUNIT_TEST(testUndoRepairDispatch);
|
||||||
@@ -1305,6 +1307,51 @@ void SwTiledRenderingTest::testUndoLimiting()
|
|||||||
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
|
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SwTiledRenderingTest::testUndoReordering()
|
||||||
|
{
|
||||||
|
// Create two views and a document of 2 paragraphs.
|
||||||
|
SwXTextDocument* pXTextDocument = createDoc();
|
||||||
|
SwWrtShell* pWrtShell1 = pXTextDocument->GetDocShell()->GetWrtShell();
|
||||||
|
int nView1 = SfxLokHelper::getView();
|
||||||
|
int nView2 = SfxLokHelper::createView();
|
||||||
|
pXTextDocument->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
|
||||||
|
SwWrtShell* pWrtShell2 = pXTextDocument->GetDocShell()->GetWrtShell();
|
||||||
|
pWrtShell2->SplitNode();
|
||||||
|
SfxLokHelper::setView(nView1);
|
||||||
|
pWrtShell1->SttEndDoc(/*bStt=*/true);
|
||||||
|
SwTextNode* pTextNode1 = pWrtShell1->GetCursor()->GetNode().GetTextNode();
|
||||||
|
// View 1 types into the first paragraph.
|
||||||
|
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'a', 0);
|
||||||
|
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'a', 0);
|
||||||
|
Scheduler::ProcessEventsToIdle();
|
||||||
|
SfxLokHelper::setView(nView2);
|
||||||
|
pWrtShell2->SttEndDoc(/*bStt=*/false);
|
||||||
|
SwTextNode* pTextNode2 = pWrtShell2->GetCursor()->GetNode().GetTextNode();
|
||||||
|
// View 2 types into the second paragraph.
|
||||||
|
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'z', 0);
|
||||||
|
pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'z', 0);
|
||||||
|
Scheduler::ProcessEventsToIdle();
|
||||||
|
CPPUNIT_ASSERT_EQUAL(OUString("a"), pTextNode1->GetText());
|
||||||
|
CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
|
||||||
|
|
||||||
|
// When view 1 presses undo:
|
||||||
|
SfxLokHelper::setView(nView1);
|
||||||
|
dispatchCommand(mxComponent, ".uno:Undo", {});
|
||||||
|
Scheduler::ProcessEventsToIdle();
|
||||||
|
|
||||||
|
// Then make sure view 1's last undo action is invoked, out of order:
|
||||||
|
// Without the accompanying fix in place, this test would have failed with:
|
||||||
|
// - Expression: pTextNode1->GetText().isEmpty()
|
||||||
|
// i.e. the "a" in the first paragraph was not removed.
|
||||||
|
CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty());
|
||||||
|
// Last undo action is not invoked, as it belongs to view 2.
|
||||||
|
CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText());
|
||||||
|
SfxLokHelper::setView(nView1);
|
||||||
|
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
|
||||||
|
SfxLokHelper::setView(nView2);
|
||||||
|
SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void SwTiledRenderingTest::testUndoShapeLimiting()
|
void SwTiledRenderingTest::testUndoShapeLimiting()
|
||||||
{
|
{
|
||||||
// Load a document and create a view.
|
// Load a document and create a view.
|
||||||
|
@@ -96,7 +96,7 @@ void SwEditShell::HandleUndoRedoContext(::sw::UndoRedoContext & rContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwEditShell::Undo(sal_uInt16 const nCount)
|
void SwEditShell::Undo(sal_uInt16 const nCount, sal_uInt16 nOffset)
|
||||||
{
|
{
|
||||||
MakeAllOutlineContentTemporarilyVisible a(GetDoc());
|
MakeAllOutlineContentTemporarilyVisible a(GetDoc());
|
||||||
|
|
||||||
@@ -132,8 +132,7 @@ void SwEditShell::Undo(sal_uInt16 const nCount)
|
|||||||
try {
|
try {
|
||||||
for (sal_uInt16 i = 0; i < nCount; ++i)
|
for (sal_uInt16 i = 0; i < nCount; ++i)
|
||||||
{
|
{
|
||||||
bRet = GetDoc()->GetIDocumentUndoRedo().Undo()
|
bRet = GetDoc()->GetIDocumentUndoRedo().UndoWithOffset(nOffset) || bRet;
|
||||||
|| bRet;
|
|
||||||
}
|
}
|
||||||
} catch (const css::uno::Exception &) {
|
} catch (const css::uno::Exception &) {
|
||||||
TOOLS_WARN_EXCEPTION("sw.core", "SwEditShell::Undo()");
|
TOOLS_WARN_EXCEPTION("sw.core", "SwEditShell::Undo()");
|
||||||
|
@@ -107,11 +107,16 @@ public:
|
|||||||
o_rpMarkList = m_pMarkList;
|
o_rpMarkList = m_pMarkList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetUndoOffset(size_t nUndoOffset) { m_nUndoOffset = nUndoOffset; }
|
||||||
|
|
||||||
|
size_t GetUndoOffset() override { return m_nUndoOffset; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SwDoc & m_rDoc;
|
SwDoc & m_rDoc;
|
||||||
IShellCursorSupplier & m_rCursorSupplier;
|
IShellCursorSupplier & m_rCursorSupplier;
|
||||||
SwFrameFormat * m_pSelFormat;
|
SwFrameFormat * m_pSelFormat;
|
||||||
SdrMarkList * m_pMarkList;
|
SdrMarkList * m_pMarkList;
|
||||||
|
size_t m_nUndoOffset = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RepeatContext final
|
class RepeatContext final
|
||||||
|
@@ -87,6 +87,8 @@ public:
|
|||||||
virtual SwRewriter GetRewriter() const override;
|
virtual SwRewriter GetRewriter() const override;
|
||||||
|
|
||||||
void SetWithRsid() { m_bWithRsid = true; }
|
void SetWithRsid() { m_bWithRsid = true; }
|
||||||
|
|
||||||
|
bool IsIndependent(const SwUndoInsert& rOther) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
SwRewriter
|
SwRewriter
|
||||||
|
@@ -80,6 +80,7 @@ public:
|
|||||||
virtual size_t GetUndoActionCount(const bool bCurrentLevel = true) const override;
|
virtual size_t GetUndoActionCount(const bool bCurrentLevel = true) const override;
|
||||||
size_t GetRedoActionCount(const bool bCurrentLevel = true) const override;
|
size_t GetRedoActionCount(const bool bCurrentLevel = true) const override;
|
||||||
void SetView(SwView* pView) override;
|
void SetView(SwView* pView) override;
|
||||||
|
bool UndoWithOffset(size_t nUndoOffset) override;
|
||||||
|
|
||||||
// SfxUndoManager
|
// SfxUndoManager
|
||||||
virtual void AddUndoAction(std::unique_ptr<SfxUndoAction> pAction,
|
virtual void AddUndoAction(std::unique_ptr<SfxUndoAction> pAction,
|
||||||
@@ -95,6 +96,12 @@ public:
|
|||||||
SwNodes & GetUndoNodes();
|
SwNodes & GetUndoNodes();
|
||||||
void SetDocShell(SwDocShell* pDocShell);
|
void SetDocShell(SwDocShell* pDocShell);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the topmost undo action owned by pView is independent from the topmost action undo
|
||||||
|
* action.
|
||||||
|
*/
|
||||||
|
bool IsViewUndoActionIndependent(const SwView* pView) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual void EmptyActionsChanged() override;
|
virtual void EmptyActionsChanged() override;
|
||||||
|
|
||||||
@@ -118,7 +125,7 @@ private:
|
|||||||
SwView* m_pView;
|
SwView* m_pView;
|
||||||
|
|
||||||
enum class UndoOrRedoType { Undo, Redo };
|
enum class UndoOrRedoType { Undo, Redo };
|
||||||
bool impl_DoUndoRedo(UndoOrRedoType undoOrRedo);
|
bool impl_DoUndoRedo(UndoOrRedoType undoOrRedo, size_t nUndoOffset);
|
||||||
|
|
||||||
// UGLY: should not be called
|
// UGLY: should not be called
|
||||||
using SdrUndoManager::Repeat;
|
using SdrUndoManager::Repeat;
|
||||||
|
@@ -41,6 +41,8 @@
|
|||||||
#include <sfx2/bindings.hxx>
|
#include <sfx2/bindings.hxx>
|
||||||
#include <osl/diagnose.h>
|
#include <osl/diagnose.h>
|
||||||
|
|
||||||
|
#include <UndoInsert.hxx>
|
||||||
|
|
||||||
using namespace ::com::sun::star;
|
using namespace ::com::sun::star;
|
||||||
|
|
||||||
// the undo array should never grow beyond this limit:
|
// the undo array should never grow beyond this limit:
|
||||||
@@ -352,6 +354,61 @@ UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter)
|
|||||||
return eUndoId;
|
return eUndoId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the topmost undo action owned by pView is independent from the topmost action undo
|
||||||
|
* action.
|
||||||
|
*/
|
||||||
|
bool UndoManager::IsViewUndoActionIndependent(const SwView* pView) const
|
||||||
|
{
|
||||||
|
if (GetUndoActionCount() <= 1 || SdrUndoManager::GetRedoActionCount() > 0)
|
||||||
|
{
|
||||||
|
// Single or less undo, owned by an other view; or redo actions that might depend on the
|
||||||
|
// current undo order.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pView)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last undo action that doesn't belong to the view.
|
||||||
|
const SfxUndoAction* pTopAction = GetUndoAction();
|
||||||
|
|
||||||
|
ViewShellId nViewId = pView->GetViewShellId();
|
||||||
|
|
||||||
|
// Earlier undo action that belongs to the view, but is not the top one.
|
||||||
|
const SfxUndoAction* pViewAction = nullptr;
|
||||||
|
const SfxUndoAction* pAction = GetUndoAction(1);
|
||||||
|
if (pAction->GetViewShellId() == nViewId)
|
||||||
|
{
|
||||||
|
pViewAction = pAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pViewAction)
|
||||||
|
{
|
||||||
|
// Found no earlier undo action that belongs to the view.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pTopSwAction = dynamic_cast<const SwUndo*>(pTopAction);
|
||||||
|
if (!pTopSwAction || pTopSwAction->GetId() != SwUndoId::TYPING)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto pViewSwAction = dynamic_cast<const SwUndo*>(pViewAction);
|
||||||
|
if (!pViewSwAction || pViewSwAction->GetId() != SwUndoId::TYPING)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& rTopInsert = *static_cast<const SwUndoInsert*>(pTopSwAction);
|
||||||
|
const auto& rViewInsert = *static_cast<const SwUndoInsert*>(pViewSwAction);
|
||||||
|
|
||||||
|
return rViewInsert.IsIndependent(rTopInsert);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
UndoManager::GetLastUndoInfo(
|
UndoManager::GetLastUndoInfo(
|
||||||
OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
|
OUString *const o_pStr, SwUndoId *const o_pId, const SwView* pView) const
|
||||||
@@ -369,7 +426,8 @@ UndoManager::GetLastUndoInfo(
|
|||||||
{
|
{
|
||||||
// If another view created the undo action, prevent undoing it from this view.
|
// If another view created the undo action, prevent undoing it from this view.
|
||||||
ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
|
ViewShellId nViewShellId = pView ? pView->GetViewShellId() : m_pDocShell->GetView()->GetViewShellId();
|
||||||
if (pAction->GetViewShellId() != nViewShellId)
|
// Unless we know that the other view's undo action is independent from us.
|
||||||
|
if (pAction->GetViewShellId() != nViewShellId && !IsViewUndoActionIndependent(pView))
|
||||||
{
|
{
|
||||||
if (o_pId)
|
if (o_pId)
|
||||||
{
|
{
|
||||||
@@ -572,7 +630,7 @@ private:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo)
|
bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo, size_t nUndoOffset)
|
||||||
{
|
{
|
||||||
SwDoc & rDoc(GetUndoNodes().GetDoc());
|
SwDoc & rDoc(GetUndoNodes().GetDoc());
|
||||||
|
|
||||||
@@ -601,6 +659,7 @@ bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo)
|
|||||||
bool bRet(false);
|
bool bRet(false);
|
||||||
|
|
||||||
::sw::UndoRedoContext context(rDoc, *pEditShell);
|
::sw::UndoRedoContext context(rDoc, *pEditShell);
|
||||||
|
context.SetUndoOffset(nUndoOffset);
|
||||||
|
|
||||||
// N.B. these may throw!
|
// N.B. these may throw!
|
||||||
if (UndoOrRedoType::Undo == undoOrRedo)
|
if (UndoOrRedoType::Undo == undoOrRedo)
|
||||||
@@ -630,7 +689,9 @@ bool UndoManager::impl_DoUndoRedo(UndoOrRedoType undoOrRedo)
|
|||||||
return bRet;
|
return bRet;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UndoManager::Undo()
|
bool UndoManager::Undo() { return UndoWithOffset(0); }
|
||||||
|
|
||||||
|
bool UndoManager::UndoWithOffset(size_t nUndoOffset)
|
||||||
{
|
{
|
||||||
if(isTextEditActive())
|
if(isTextEditActive())
|
||||||
{
|
{
|
||||||
@@ -638,7 +699,7 @@ bool UndoManager::Undo()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return impl_DoUndoRedo(UndoOrRedoType::Undo);
|
return impl_DoUndoRedo(UndoOrRedoType::Undo, nUndoOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +711,7 @@ bool UndoManager::Redo()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return impl_DoUndoRedo(UndoOrRedoType::Redo);
|
return impl_DoUndoRedo(UndoOrRedoType::Redo, /*nUndoOffset=*/0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -476,6 +476,11 @@ SwRewriter SwUndoInsert::GetRewriter() const
|
|||||||
return aResult;
|
return aResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SwUndoInsert::IsIndependent(const SwUndoInsert& rOther) const
|
||||||
|
{
|
||||||
|
return m_nNode != rOther.m_nNode;
|
||||||
|
}
|
||||||
|
|
||||||
class SwUndoReplace::Impl
|
class SwUndoReplace::Impl
|
||||||
: private SwUndoSaveContent
|
: private SwUndoSaveContent
|
||||||
{
|
{
|
||||||
|
@@ -374,7 +374,7 @@ typedef bool (SwWrtShell::*FNSimpleMove)();
|
|||||||
|
|
||||||
enum class FieldDialogPressedButton { NONE, Previous, Next };
|
enum class FieldDialogPressedButton { NONE, Previous, Next };
|
||||||
|
|
||||||
void Do( DoType eDoType, sal_uInt16 nCnt = 1 );
|
void Do(DoType eDoType, sal_uInt16 nCnt = 1, sal_uInt16 nOffset = 0);
|
||||||
OUString GetDoString( DoType eDoType ) const;
|
OUString GetDoString( DoType eDoType ) const;
|
||||||
OUString GetRepeatString() const;
|
OUString GetRepeatString() const;
|
||||||
void GetDoStrings( DoType eDoType, SfxStringListItem& rStrLstItem ) const;
|
void GetDoStrings( DoType eDoType, SfxStringListItem& rStrLstItem ) const;
|
||||||
|
@@ -101,6 +101,7 @@
|
|||||||
#include <UndoTable.hxx>
|
#include <UndoTable.hxx>
|
||||||
|
|
||||||
#include <ndtxt.hxx>
|
#include <ndtxt.hxx>
|
||||||
|
#include <UndoManager.hxx>
|
||||||
|
|
||||||
FlyMode SwBaseShell::eFrameMode = FLY_DRAG_END;
|
FlyMode SwBaseShell::eFrameMode = FLY_DRAG_END;
|
||||||
|
|
||||||
@@ -618,7 +619,25 @@ void SwBaseShell::ExecUndo(SfxRequest &rReq)
|
|||||||
{
|
{
|
||||||
for (SwViewShell& rShell : rWrtShell.GetRingContainer())
|
for (SwViewShell& rShell : rWrtShell.GetRingContainer())
|
||||||
rShell.LockPaint();
|
rShell.LockPaint();
|
||||||
rWrtShell.Do( SwWrtShell::UNDO, nCnt );
|
|
||||||
|
sal_uInt16 nUndoOffset = 0;
|
||||||
|
if (comphelper::LibreOfficeKit::isActive() && !bRepair && nCnt == 1)
|
||||||
|
{
|
||||||
|
sw::UndoManager& rManager = rWrtShell.GetDoc()->GetUndoManager();
|
||||||
|
const SfxUndoAction* pAction = rManager.GetUndoAction();
|
||||||
|
SwView& rView = rWrtShell.GetView();
|
||||||
|
ViewShellId nViewShellId = rView.GetViewShellId();
|
||||||
|
if (pAction->GetViewShellId() != nViewShellId
|
||||||
|
&& rManager.IsViewUndoActionIndependent(&rView))
|
||||||
|
{
|
||||||
|
// Execute the undo with an offset: don't undo the top action, but an
|
||||||
|
// earlier one, since it's independent and that belongs to our view.
|
||||||
|
nUndoOffset = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rWrtShell.Do(SwWrtShell::UNDO, nCnt, nUndoOffset);
|
||||||
|
|
||||||
for (SwViewShell& rShell : rWrtShell.GetRingContainer())
|
for (SwViewShell& rShell : rWrtShell.GetRingContainer())
|
||||||
rShell.UnlockPaint();
|
rShell.UnlockPaint();
|
||||||
|
|
||||||
|
@@ -30,7 +30,7 @@
|
|||||||
// Undo ends all modes. If a selection is emerged by the Undo,
|
// Undo ends all modes. If a selection is emerged by the Undo,
|
||||||
// this must be considered for further action.
|
// this must be considered for further action.
|
||||||
|
|
||||||
void SwWrtShell::Do( DoType eDoType, sal_uInt16 nCnt )
|
void SwWrtShell::Do(DoType eDoType, sal_uInt16 nCnt, sal_uInt16 nOffset)
|
||||||
{
|
{
|
||||||
// #105332# save current state of DoesUndo()
|
// #105332# save current state of DoesUndo()
|
||||||
bool bSaveDoesUndo = DoesUndo();
|
bool bSaveDoesUndo = DoesUndo();
|
||||||
@@ -42,7 +42,7 @@ void SwWrtShell::Do( DoType eDoType, sal_uInt16 nCnt )
|
|||||||
DoUndo(false); // #i21739#
|
DoUndo(false); // #i21739#
|
||||||
// Reset modes
|
// Reset modes
|
||||||
EnterStdMode();
|
EnterStdMode();
|
||||||
SwEditShell::Undo(nCnt);
|
SwEditShell::Undo(nCnt, nOffset);
|
||||||
break;
|
break;
|
||||||
case REDO:
|
case REDO:
|
||||||
DoUndo(false); // #i21739#
|
DoUndo(false); // #i21739#
|
||||||
|
Reference in New Issue
Block a user