sc/qa/unit/tiledrendering/tiledrendering.cxx |   80 +++++++++++++++++++++++++++
 sc/source/ui/inc/undomanager.hxx             |    2 
 sc/source/ui/undo/undobase.cxx               |   44 +++++++++++---
 3 files changed, 116 insertions(+), 10 deletions(-)

New commits:
commit 80cdfe4512a66ab58e0ae27c9fafad601c2f84b2
Author:     Noel Grandin <noelgran...@gmail.com>
AuthorDate: Sat Jul 30 09:02:31 2022 +0200
Commit:     Noel Grandin <noel.gran...@collabora.co.uk>
CommitDate: Sun Jul 31 09:27:23 2022 +0200

    sc, out of order undo: allow a subset of a non-empty redo list
    
    This is the calc analogue of
        commit 60665dc4a2af238939b1a5056ae4a4ce2c083159
        sw, out of order undo: allow a subset of a non-empty redo list
    
    Specifically, we used to not allow out of order undo at all if the redo
    list was non-empty. This relaxes that condition a bit. Out of order undo
    is OK with a non-empty redo list, in case all undo actions in the redo
    list are either
    
    1) owned by the current view or
    
    2) independent from the undo action to be executed
    
    I.e. if view1 has lots of type undo actions and an view2 adds a
    single type undo action on top of it, then allow view 1 to execute
    multiple of its typing undo actions, not just a single one.
    
    Change-Id: Ib7ab88e9ebbefce267d3c7d567debd1cd98d7d6d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/137628
    Tested-by: Jenkins
    Reviewed-by: Noel Grandin <noel.gran...@collabora.co.uk>

diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index f8498fae1b95..9ed5cf471269 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -129,6 +129,7 @@ public:
     void testCommentCellCopyPaste();
     void testInvalidEntrySave();
     void testUndoReordering();
+    void testUndoReorderingRedo();
 
     CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
     CPPUNIT_TEST(testRowColumnHeaders);
@@ -187,6 +188,7 @@ public:
     CPPUNIT_TEST(testCommentCellCopyPaste);
     CPPUNIT_TEST(testInvalidEntrySave);
     CPPUNIT_TEST(testUndoReordering);
+    CPPUNIT_TEST(testUndoReorderingRedo);
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -3047,6 +3049,84 @@ void ScTiledRenderingTest::testUndoReordering()
     CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
 }
 
+void ScTiledRenderingTest::testUndoReorderingRedo()
+{
+    ScModelObj* pModelObj = createDoc("empty.ods");
+    CPPUNIT_ASSERT(pModelObj);
+    ScDocument* pDoc = pModelObj->GetDocument();
+    CPPUNIT_ASSERT(pDoc);
+    ScUndoManager* pUndoManager = pDoc->GetUndoManager();
+    CPPUNIT_ASSERT(pUndoManager);
+    CPPUNIT_ASSERT_EQUAL(std::size_t(0), pUndoManager->GetUndoActionCount());
+
+    // view #1
+    int nView1 = SfxLokHelper::getView();
+    ViewCallback aView1;
+
+    // view #2
+    SfxLokHelper::createView();
+    int nView2 = SfxLokHelper::getView();
+    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    ViewCallback aView2;
+
+    // text edit a cell in view #1
+    SfxLokHelper::setView(nView1);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
+
+    // text edit another cell in view #1
+    SfxLokHelper::setView(nView1);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'y', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'y', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
+    CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
+
+    // text edit a different cell in view #2
+    SfxLokHelper::setView(nView2);
+    ScTabViewShell* pView2 = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    pView2->SetCursor(0, 2);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'C', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'C', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::RETURN);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::RETURN);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(std::size_t(3), pUndoManager->GetUndoActionCount());
+    CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString("yy"), pDoc->GetString(ScAddress(0, 1, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
+
+    // View 1 presses undo, and the second cell is erased
+    SfxLokHelper::setView(nView1);
+    dispatchCommand(mxComponent, ".uno:Undo", {});
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(std::size_t(2), pUndoManager->GetUndoActionCount());
+    CPPUNIT_ASSERT_EQUAL(OUString("xx"), pDoc->GetString(ScAddress(0, 0, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
+
+    // View 1 presses undo again, and the first cell is erased
+    dispatchCommand(mxComponent, ".uno:Undo", {});
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT_EQUAL(std::size_t(1), pUndoManager->GetUndoActionCount());
+    CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 0, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString(""), pDoc->GetString(ScAddress(0, 1, 0)));
+    CPPUNIT_ASSERT_EQUAL(OUString("CC"), pDoc->GetString(ScAddress(0, 2, 0)));
+}
+
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
diff --git a/sc/source/ui/inc/undomanager.hxx b/sc/source/ui/inc/undomanager.hxx
index 4f3ddc282fb4..da254a02d294 100644
--- a/sc/source/ui/inc/undomanager.hxx
+++ b/sc/source/ui/inc/undomanager.hxx
@@ -11,6 +11,7 @@
 #include <svx/sdrundomanager.hxx>
 
 class SfxViewShell;
+class ScUndoEnterData;
 
 class SC_DLLPUBLIC ScUndoManager : public SdrUndoManager
 {
@@ -29,6 +30,7 @@ public:
 
 private:
     static std::optional<ScRange> getAffectedRangeFromUndo(const 
SfxUndoAction*);
+    static const ScUndoEnterData* getScUndoEnterData(const SfxUndoAction*);
 };
 
 class ScUndoRedoContext final : public SfxUndoContext
diff --git a/sc/source/ui/undo/undobase.cxx b/sc/source/ui/undo/undobase.cxx
index b591b38aabc7..250c78619ef8 100644
--- a/sc/source/ui/undo/undobase.cxx
+++ b/sc/source/ui/undo/undobase.cxx
@@ -623,10 +623,9 @@ ScUndoManager::~ScUndoManager() {}
  */
 bool ScUndoManager::IsViewUndoActionIndependent(const SfxViewShell* 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;
     }
 
@@ -662,21 +661,46 @@ bool ScUndoManager::IsViewUndoActionIndependent(const 
SfxViewShell* pView) const
     if (!viewRange)
         return false;
 
-    return !topRange->Intersects(*viewRange);
+    if (topRange->Intersects(*viewRange))
+        return false;
+
+    for (size_t i = 0; i < GetRedoActionCount(); ++i)
+    {
+        auto pRedoAction = getScUndoEnterData(GetRedoAction(i));
+        if (!pRedoAction)
+        {
+            return false;
+        }
+        std::optional<ScRange> redoRange = 
getAffectedRangeFromUndo(pRedoAction);
+        if (!redoRange || (redoRange->Intersects(*viewRange) && 
pRedoAction->GetViewShellId() != nViewId))
+        {
+            // Dependent redo action and owned by another view.
+            return false;
+        }
+    }
+
+    return true;
 }
 
 std::optional<ScRange> ScUndoManager::getAffectedRangeFromUndo(const 
SfxUndoAction* pAction)
 {
-    auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction);
-    if (!pListAction)
-        return std::nullopt;
-    if (pListAction->maUndoActions.size() > 1)
-        return std::nullopt;
-    auto pTopScUndoEnterData = 
dynamic_cast<ScUndoEnterData*>(pListAction->maUndoActions[0].pAction.get());
+    auto pTopScUndoEnterData = getScUndoEnterData(pAction);
     if (!pTopScUndoEnterData)
         return std::nullopt;
     return pTopScUndoEnterData->GetPositionAddress();
 }
 
+const ScUndoEnterData* ScUndoManager::getScUndoEnterData(const SfxUndoAction* 
pAction)
+{
+    const ScUndoEnterData* pUndoEnterData = dynamic_cast<const 
ScUndoEnterData*>(pAction);
+    if (pUndoEnterData)
+        return pUndoEnterData;
+    auto pListAction = dynamic_cast<const SfxListUndoAction*>(pAction);
+    if (!pListAction)
+        return nullptr;
+    if (pListAction->maUndoActions.size() > 1)
+        return nullptr;
+    return 
dynamic_cast<ScUndoEnterData*>(pListAction->maUndoActions[0].pAction.get());
+}
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Reply via email to