editeng/source/editeng/editview.cxx          |    5 -
 include/vcl/window.hxx                       |    2 
 sc/qa/unit/tiledrendering/tiledrendering.cxx |  115 +++++++++++++++++++++++++++
 sc/source/ui/inc/gridwin.hxx                 |    1 
 sc/source/ui/view/gridwin4.cxx               |   26 ++++++
 vcl/source/window/paint.cxx                  |    5 +
 6 files changed, 153 insertions(+), 1 deletion(-)

New commits:
commit 8a732d9ff15ba73835c3fa60208f954b772f5a9c
Author:     Marco Cecchetti <marco.cecche...@collabora.com>
AuthorDate: Mon Dec 4 09:31:23 2023 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Dec 5 11:08:34 2023 +0100

    calc: on editing invalidation of view with different zoom is wrong
    
    This patch fixes the following invalidation issue:
    
    There are 2 views with different zoom levels.
    In a view text editing for a cell occurs.
    The other view is not invalidated properly: the computed invalidation
    rectangle is misplaced.
    
    Change-Id: I72db61486647640ee68e6cb2db96b2902de5b997
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160303
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/editeng/source/editeng/editview.cxx 
b/editeng/source/editeng/editview.cxx
index 0f53a53de045..6cb2913be3da 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -231,7 +231,10 @@ void EditView::InvalidateOtherViewWindows( const 
tools::Rectangle& rInvRect )
         for (auto& pWin : pImpEditView->aOutWindowSet)
         {
             if (pWin)
-                pWin->Invalidate( bNegativeX ? lcl_negateRectX(rInvRect) : 
rInvRect );
+            {
+                if (!pWin->InvalidateByForeignEditView(this))
+                    pWin->Invalidate( bNegativeX ? lcl_negateRectX(rInvRect) : 
rInvRect );
+            }
         }
     }
 }
diff --git a/include/vcl/window.hxx b/include/vcl/window.hxx
index d03555bd5b99..829b9f174b57 100644
--- a/include/vcl/window.hxx
+++ b/include/vcl/window.hxx
@@ -62,6 +62,7 @@ class VclWindowEvent;
 class AllSettings;
 class InputContext;
 class VclEventListeners;
+class EditView;
 enum class ImplPaintFlags;
 enum class VclEventId;
 enum class PointerStyle;
@@ -970,6 +971,7 @@ public:
      */
     virtual void                        LogicInvalidate(const 
tools::Rectangle* pRectangle);
 
+    virtual bool                        InvalidateByForeignEditView(EditView* 
);
     /**
      * Notification about some rectangle of the output device got invalidated. 
Used for the
      * dialogs and floating windows (e.g. context menu, popup).
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 1f18401a970c..7d89cb787c85 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -9,6 +9,7 @@
 
 #include <test/unoapixml_test.hxx>
 #include <test/helper/transferable.hxx>
+#include <cppunit/tools/StringHelper.h>
 #include <boost/property_tree/json_parser.hpp>
 
 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
@@ -60,6 +61,31 @@ static std::ostream& operator<<(std::ostream& os, 
ViewShellId const & id)
     os << static_cast<sal_Int32>(id); return os;
 }
 
+// for passing data to testInvalidateOnTextEditWithDifferentZoomLevels
+struct ColRowZoom
+{
+    SCCOL col;
+    SCROW row;
+    int zoom;
+};
+
+CPPUNIT_NS_BEGIN
+namespace StringHelper
+{
+// used by CPPUNIT_TEST_PARAMETERIZED for 
testInvalidateOnTextEditWithDifferentZoomLevels
+template<>
+inline std::string toString(const ColRowZoom& item)
+{
+    std::ostringstream ss;
+    ss << "zoom level: " << item.zoom << ", "
+       << "col: " << item.col << ", "
+       << "row: " << item.row;
+    return ss.str();
+}
+
+}
+CPPUNIT_NS_END
+
 namespace
 {
 
@@ -129,6 +155,7 @@ public:
     void testUndoReorderingRedo();
     void testUndoReorderingMulti();
     void testGetViewRenderState();
+    void testInvalidateOnTextEditWithDifferentZoomLevels(const ColRowZoom& 
rData);
 
     CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
     CPPUNIT_TEST(testRowColumnHeaders);
@@ -190,6 +217,14 @@ public:
     CPPUNIT_TEST(testUndoReorderingRedo);
     CPPUNIT_TEST(testUndoReorderingMulti);
     CPPUNIT_TEST(testGetViewRenderState);
+    CPPUNIT_TEST_PARAMETERIZED(testInvalidateOnTextEditWithDifferentZoomLevels,
+                               std::initializer_list<ColRowZoom>
+                               {
+                                   // zoom level 120%
+                                   {0, 999, 1}, {99, 0, 1},
+                                   // zoom level 40%
+                                   {0, 999, -5}, {99, 0, -5}
+                               });
     CPPUNIT_TEST_SUITE_END();
 
 private:
@@ -3286,6 +3321,86 @@ void ScTiledRenderingTest::testGetViewRenderState()
     CPPUNIT_ASSERT_EQUAL(OString(";Default"), pModelObj->getViewRenderState());
 }
 
+/*
+ * testInvalidateOnTextEditWithDifferentZoomLevels
+ * steps:
+ * set view 1 zoom to the passed zoom level
+ * in view 1 type a char at the passed cell address
+ * store invalidation rectangle
+ * exit from in place editing (press esc)
+ * create view 2 (keep 100% zoom)
+ * go to the same cell address used in view 1
+ * type a char into the cell
+ * get invalidation rectangle for view 1
+ * check if the invalidation rectangle is equal to the one stored previously
+*/
+void 
ScTiledRenderingTest::testInvalidateOnTextEditWithDifferentZoomLevels(const 
ColRowZoom& rData)
+{
+    ScModelObj* pModelObj = createDoc("empty.ods");
+    CPPUNIT_ASSERT(pModelObj);
+    ScDocument* pDoc = pModelObj->GetDocument();
+    CPPUNIT_ASSERT(pDoc);
+
+    OUString sZoomUnoCmd = ".uno:ZoomPlus";
+    int nZoomLevel = rData.zoom;
+    if (nZoomLevel < 0)
+    {
+        nZoomLevel = -nZoomLevel;
+        sZoomUnoCmd = ".uno:ZoomMinus";
+    }
+
+    // view #1
+    ViewCallback aView1;
+    // set zoom level
+    for (int i = 0; i < nZoomLevel; ++i)
+        dispatchCommand(mxComponent, sZoomUnoCmd, {});
+    Scheduler::ProcessEventsToIdle();
+
+    auto* pTabViewShell1 = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pTabViewShell1);
+
+    // enable in place editing in view 1
+    auto& rInvalidations = aView1.m_aInvalidations;
+    pTabViewShell1->SetCursor(rData.col, rData.row);
+    Scheduler::ProcessEventsToIdle();
+    aView1.m_bInvalidateTiles = false;
+    rInvalidations.clear();
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
+    CPPUNIT_ASSERT(!rInvalidations.empty());
+    tools::Rectangle aInvRect1 = rInvalidations[0];
+
+    // end editing
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
+    Scheduler::ProcessEventsToIdle();
+
+    // view #2
+    SfxLokHelper::createView();
+    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    ViewCallback aView2;
+    Scheduler::ProcessEventsToIdle();
+
+    auto* pTabViewShell2 = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pTabViewShell2);
+    pTabViewShell2->SetCursor(rData.col, rData.row);
+    Scheduler::ProcessEventsToIdle();
+
+    // text edit in view #2
+    aView1.m_bInvalidateTiles = false;
+    rInvalidations.clear();
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+    Scheduler::ProcessEventsToIdle();
+    CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
+    CPPUNIT_ASSERT(!rInvalidations.empty());
+    tools::Rectangle aInvRect2 = rInvalidations[0];
+
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalidation rectangle is wrong.", 
aInvRect1, aInvRect2);
+}
+
 }
 
 CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
index 8334e5fd0d51..048d2d3d9359 100644
--- a/sc/source/ui/inc/gridwin.hxx
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -380,6 +380,7 @@ public:
     void LogicInvalidate(const tools::Rectangle* pRectangle) override;
     void LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart);
 
+    bool InvalidateByForeignEditView(EditView* pEditView) override;
     /// Update the cell selection according to what handles have been dragged.
     /// @see vcl::ITiledRenderable::setTextSelection() for the values of nType.
     /// Coordinates are in pixels.
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index 1f235fcd6aa4..390c4b2bdb63 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -671,6 +671,11 @@ int lcl_GetMultiLineHeight(EditEngine* pEditEngine)
 
     return nHeight;
 }
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+    return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), 
rRect.Bottom());
+}
 }
 
 void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& 
rTableInfo, ScOutputData& aOutputData,
@@ -1765,6 +1770,27 @@ void ScGridWindow::LogicInvalidate(const 
tools::Rectangle* pRectangle)
     LogicInvalidatePart(pRectangle, pViewShell->getPart());
 }
 
+bool ScGridWindow::InvalidateByForeignEditView(EditView* pEditView)
+{
+    if (!pEditView)
+        return false;
+
+    auto* pGridWin = dynamic_cast<ScGridWindow*>(pEditView->GetWindow());
+    if (!pGridWin)
+        return false;
+
+    const ScViewData& rViewData = pGridWin->getViewData();
+    tools::Long nRefTabNo = rViewData.GetRefTabNo();
+    tools::Long nX = rViewData.GetCurXForTab(nRefTabNo);
+    tools::Long nY = rViewData.GetCurYForTab(nRefTabNo);
+
+    tools::Rectangle aPixRect = getViewData().GetEditArea(eWhich, nX, nY, 
this, nullptr, true);
+    tools::Rectangle aLogicRect = PixelToLogic(aPixRect, 
getViewData().GetLogicMode());
+    Invalidate(pEditView->IsNegativeX() ? lcl_negateRectX(aLogicRect) : 
aLogicRect);
+
+    return true;
+}
+
 void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
 {
     ScTabView* pTabView = mrViewData.GetView();
diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx
index a70f1c0e4004..2e9b153bc961 100644
--- a/vcl/source/window/paint.cxx
+++ b/vcl/source/window/paint.cxx
@@ -1198,6 +1198,11 @@ void Window::LogicInvalidate(const tools::Rectangle* 
pRectangle)
         PixelInvalidate(nullptr);
 }
 
+bool Window::InvalidateByForeignEditView(EditView* )
+{
+    return false;
+}
+
 void Window::PixelInvalidate(const tools::Rectangle* pRectangle)
 {
     if (comphelper::LibreOfficeKit::isDialogPainting() || 
!comphelper::LibreOfficeKit::isActive())

Reply via email to