diff options
-rw-r--r-- | include/svl/undo.hxx | 2 | ||||
-rw-r--r-- | svl/source/undo/undo.cxx | 4 | ||||
-rw-r--r-- | sw/qa/extras/tiledrendering/tiledrendering.cxx | 56 | ||||
-rw-r--r-- | sw/source/core/undo/docundo.cxx | 21 |
4 files changed, 77 insertions, 6 deletions
diff --git a/include/svl/undo.hxx b/include/svl/undo.hxx index e0d064b27987..eff352298a5c 100644 --- a/include/svl/undo.hxx +++ b/include/svl/undo.hxx @@ -206,7 +206,7 @@ public: OUString GetUndoActionsInfo() const; virtual size_t GetRedoActionCount( bool const i_currentLevel = CurrentLevel ) const; OUString GetRedoActionComment( size_t nNo=0, bool const i_currentLevel = CurrentLevel ) const; - SfxUndoAction* GetRedoAction() const; + SfxUndoAction* GetRedoAction(size_t nNo = 0) const; /// Get info about all redo actions (comment, view shell id, etc.) OUString GetRedoActionsInfo() const; virtual bool Undo(); diff --git a/svl/source/undo/undo.cxx b/svl/source/undo/undo.cxx index 0c081b152c3a..f84d2cda185a 100644 --- a/svl/source/undo/undo.cxx +++ b/svl/source/undo/undo.cxx @@ -747,7 +747,7 @@ size_t SfxUndoManager::ImplGetRedoActionCount_Lock( bool const i_currentLevel ) } -SfxUndoAction* SfxUndoManager::GetRedoAction() const +SfxUndoAction* SfxUndoManager::GetRedoAction(size_t nNo) const { UndoManagerGuard aGuard( *m_xData ); @@ -756,7 +756,7 @@ SfxUndoAction* SfxUndoManager::GetRedoAction() const { return nullptr; } - return pUndoArray->maUndoActions[ pUndoArray->nCurUndoAction ].pAction.get(); + return pUndoArray->maUndoActions[pUndoArray->nCurUndoAction + nNo].pAction.get(); } diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx index 46f1c781be02..4f0c7e3c5c7b 100644 --- a/sw/qa/extras/tiledrendering/tiledrendering.cxx +++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx @@ -109,6 +109,7 @@ public: void testUndoInvalidations(); void testUndoLimiting(); void testUndoReordering(); + void testUndoReorderingRedo(); void testUndoShapeLimiting(); void testUndoDispatch(); void testUndoRepairDispatch(); @@ -191,6 +192,7 @@ public: CPPUNIT_TEST(testUndoInvalidations); CPPUNIT_TEST(testUndoLimiting); CPPUNIT_TEST(testUndoReordering); + CPPUNIT_TEST(testUndoReorderingRedo); CPPUNIT_TEST(testUndoShapeLimiting); CPPUNIT_TEST(testUndoDispatch); CPPUNIT_TEST(testUndoRepairDispatch); @@ -1352,6 +1354,60 @@ void SwTiledRenderingTest::testUndoReordering() SfxViewShell::Current()->setLibreOfficeKitViewCallback(nullptr); } +void SwTiledRenderingTest::testUndoReorderingRedo() +{ + // 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, twice. + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'f', 0); + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 'f', 0); + Scheduler::ProcessEventsToIdle(); + // Go to the start of the paragraph, to avoid grouping. + pWrtShell1->SttEndDoc(/*bStt=*/true); + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 's', 0); + pXTextDocument->postKeyEvent(LOK_KEYEVENT_KEYUP, 's', 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("sf"), pTextNode1->GetText()); + CPPUNIT_ASSERT_EQUAL(OUString("z"), pTextNode2->GetText()); + + // When view 1 presses undo, twice: + SfxLokHelper::setView(nView1); + dispatchCommand(mxComponent, ".uno:Undo", {}); + Scheduler::ProcessEventsToIdle(); + // First just s(econd) is erased: + CPPUNIT_ASSERT_EQUAL(OUString("f"), pTextNode1->GetText()); + dispatchCommand(mxComponent, ".uno:Undo", {}); + Scheduler::ProcessEventsToIdle(); + + // Then make sure view 1's undo actions are invoked, out of order: + // Without the accompanying fix in place, this test would have failed with: + // - Expression: pTextNode1->GetText().isEmpty() + // i.e. out of order undo was executed only once, not twice. + CPPUNIT_ASSERT(pTextNode1->GetText().isEmpty()); + // The top 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() { // Load a document and create a view. diff --git a/sw/source/core/undo/docundo.cxx b/sw/source/core/undo/docundo.cxx index 0a2b145362f4..8a4f6450605c 100644 --- a/sw/source/core/undo/docundo.cxx +++ b/sw/source/core/undo/docundo.cxx @@ -360,10 +360,9 @@ UndoManager::EndUndo(SwUndoId eUndoId, SwRewriter const*const pRewriter) */ bool UndoManager::IsViewUndoActionIndependent(const SwView* pView) const { - if (GetUndoActionCount() <= 1 || SdrUndoManager::GetRedoActionCount() > 0) + if (GetUndoActionCount() <= 1) { - // Single or less undo, owned by another view; or redo actions that might depend on the - // current undo order. + // Single or less undo, owned by another view. return false; } @@ -406,6 +405,22 @@ bool UndoManager::IsViewUndoActionIndependent(const SwView* pView) const const auto& rTopInsert = *static_cast<const SwUndoInsert*>(pTopSwAction); const auto& rViewInsert = *static_cast<const SwUndoInsert*>(pViewSwAction); + for (size_t i = 0; i < GetRedoActionCount(); ++i) + { + auto pRedoAction = dynamic_cast<const SwUndo*>(GetRedoAction(i)); + if (!pRedoAction || pViewSwAction->GetId() != SwUndoId::TYPING) + { + return false; + } + + const auto& rRedoInsert = *static_cast<const SwUndoInsert*>(pRedoAction); + if (!rViewInsert.IsIndependent(rRedoInsert) && rRedoInsert.GetViewShellId() != nViewId) + { + // Dependent redo action and owned by an other view. + return false; + } + } + return rViewInsert.IsIndependent(rTopInsert); } |