include/test/unoapi_test.hxx                                           |   12 
 sc/inc/hints.hxx                                                       |    4 
 sc/qa/unit/helper/qahelper.hxx                                         |   11 
 sc/qa/unit/tiledrendering/data/cell-invalidations-200zoom-settings.ods |binary
 sc/qa/unit/tiledrendering/data/cell-invalidations.ods                  |binary
 sc/qa/unit/tiledrendering/tiledrendering.cxx                           |  343 
+++++++---
 sc/source/core/tool/hints.cxx                                          |    5 
 sc/source/ui/docshell/docfunc.cxx                                      |   27 
 sc/source/ui/docshell/docsh3.cxx                                       |   41 -
 sc/source/ui/inc/docsh.hxx                                             |   11 
 sc/source/ui/inc/tabview.hxx                                           |    5 
 sc/source/ui/unoobj/docuno.cxx                                         |    2 
 sc/source/ui/view/tabview.cxx                                          |   12 
 sc/source/ui/view/tabview3.cxx                                         |   67 +
 sc/source/ui/view/tabvwsh5.cxx                                         |    7 
 sc/source/ui/view/viewfun3.cxx                                         |   18 
 16 files changed, 422 insertions(+), 143 deletions(-)

New commits:
commit 7e774c56ef6e261e92e78db52ccf79b046ab2a98
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Mon Jan 15 10:46:42 2024 +0000
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Jan 23 22:11:31 2024 +0100

    don't always invalidate the entire width of the calc window
    
    If know the max width affected we can avoid redrawing much of
    the row
    
    LTR cell edits, deletes, single line paste
    
    Change-Id: Ib7e3d8bfa3a5ce7df97f28bcf7858b3abcb752a4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162408
    Tested-by: Jenkins
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162483
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/sc/inc/hints.hxx b/sc/inc/hints.hxx
index d890072c1f81..00d6d80e0fa3 100644
--- a/sc/inc/hints.hxx
+++ b/sc/inc/hints.hxx
@@ -28,10 +28,11 @@ class SC_DLLPUBLIC ScPaintHint final : public SfxHint
 {
     ScRange         aRange;
     PaintPartFlags  nParts;
+    tools::Long nWidthAffectedHint;
 
 public:
                     ScPaintHint() = delete;
-                    ScPaintHint( const ScRange& rRng, PaintPartFlags nPaint );
+                    ScPaintHint( const ScRange& rRng, PaintPartFlags nPaint, 
tools::Long nMaxWidthAffectedHint = -1);
                     virtual ~ScPaintHint() override;
 
     SCCOL           GetStartCol() const     { return aRange.aStart.Col(); }
@@ -41,6 +42,7 @@ public:
     SCROW           GetEndRow() const       { return aRange.aEnd.Row(); }
     SCTAB           GetEndTab() const       { return aRange.aEnd.Tab(); }
     PaintPartFlags  GetParts() const        { return nParts; }
+    tools::Long     GetMaxWidthAffectedHint() const { return 
nWidthAffectedHint; }
 };
 
 class ScUpdateRefHint final : public SfxHint
diff --git a/sc/qa/unit/tiledrendering/data/cell-invalidations.ods 
b/sc/qa/unit/tiledrendering/data/cell-invalidations.ods
new file mode 100644
index 000000000000..0f117775a467
Binary files /dev/null and 
b/sc/qa/unit/tiledrendering/data/cell-invalidations.ods differ
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index d645d6f28dbc..93b532d02b6a 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -92,6 +92,10 @@ inline std::string toString(const ColRowZoom& item)
 }
 CPPUNIT_NS_END
 
+namespace {
+class ViewCallback;
+}
+
 class ScTiledRenderingTest : public UnoApiXmlTest
 {
 public:
@@ -99,6 +103,11 @@ public:
     virtual void setUp() override;
     virtual void tearDown() override;
 
+    void checkSampleInvalidation(const ViewCallback& rView, bool bFullRow);
+    void cellInvalidationHelper(ScModelObj* pModelObj, ScTabViewShell* pView,
+                                const ScAddress& rAdr, bool bAddText,
+                                bool bFullRow);
+
     ScModelObj* createDoc(const char* pName);
     void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
     static void callback(int nType, const char* pPayload, void* pData);
@@ -3354,6 +3363,112 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testNoInvalidateOnSave)
     CPPUNIT_ASSERT(!aView.m_bInvalidateTiles);
 }
 
+void ScTiledRenderingTest::checkSampleInvalidation(const ViewCallback& rView, 
bool bFullRow)
+{
+    // we expect invalidations, but that isn't really important
+    CPPUNIT_ASSERT(rView.m_bInvalidateTiles);
+    tools::Rectangle aInvalidation;
+    for (const auto& rRect : rView.m_aInvalidations)
+        aInvalidation.Union(rRect);
+    if (!bFullRow)
+    {
+        // What matters is that we expect that the invalidation does not 
extend all the
+        // way to the max right of the sheet.
+        // Here we originally got 32212306 and now ~5056 for a single cell case
+        CPPUNIT_ASSERT_LESSEQUAL(tools::Long(8000), aInvalidation.GetWidth());
+    }
+    else
+    {
+        // We expect RTL to continue to invalidate the entire row
+        // from 0 to end of sheet (see ScDocShell::PostPaint, 'Extend to whole 
rows'),
+        // which is different to the adjusted LTR case which
+        // invalidated the row from left of edited cell to right of end
+        // of sheet.
+        CPPUNIT_ASSERT_LESSEQUAL(tools::Long(0), aInvalidation.Left());
+        CPPUNIT_ASSERT_EQUAL(tools::Long(32212230), aInvalidation.Right());
+    }
+}
+
+void ScTiledRenderingTest::cellInvalidationHelper(ScModelObj* pModelObj, 
ScTabViewShell* pView, const ScAddress& rAdr,
+                                                  bool bAddText, bool bFullRow)
+{
+    // view
+    ViewCallback aView;
+
+    if (bAddText)
+    {
+        // Type "Hello World" in D8, process events to idle and don't commit 
yet
+        lcl_typeCharsInCell("Hello World", rAdr.Col(), rAdr.Row(), pView, 
pModelObj, false, false);
+
+        aView.m_bInvalidateTiles = false;
+        aView.m_aInvalidations.clear();
+
+        // commit text and process events to idle
+        lcl_typeCharsInCell("", rAdr.Col(), rAdr.Row(), pView, pModelObj, 
true, true);
+    }
+    else // DeleteText
+    {
+        pView->SetCursor(rAdr.Col(), rAdr.Row());
+        pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
+        pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
+        Scheduler::ProcessEventsToIdle();
+    }
+
+    checkSampleInvalidation(aView, bFullRow);
+}
+
+CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testCellMinimalInvalidations)
+{
+    ScAddress aA8(0, 7, 0);
+    ScAddress aD4(3, 7, 0);
+    ScAddress aD13(3, 12, 0);
+    ScAddress aD17(3, 16, 0);
+
+    ScModelObj* pModelObj = createDoc("cell-invalidations.ods");
+    CPPUNIT_ASSERT(pModelObj);
+    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pView);
+
+    // Changed: Minimized invalidations (bFullRow: false)
+
+    // Common case, LTR, default cell formatting
+    cellInvalidationHelper(pModelObj, pView, aA8, true, false);
+    cellInvalidationHelper(pModelObj, pView, aD4, true, false);
+    // Left-aligned merged cells
+    cellInvalidationHelper(pModelObj, pView, aD17, true, false);
+    // Delete single cell text case
+    cellInvalidationHelper(pModelObj, pView, aA8, false, false);
+    // Paste into a single cell
+    {
+        pView->SetCursor(aD4.Col(), aD4.Row());
+        uno::Sequence<beans::PropertyValue> aArgs;
+        dispatchCommand(mxComponent, ".uno:Copy", aArgs);
+        pView->SetCursor(aA8.Col(), aA8.Row());
+        Scheduler::ProcessEventsToIdle();
+
+        ViewCallback aView;
+        dispatchCommand(mxComponent, ".uno:Paste", aArgs);
+        Scheduler::ProcessEventsToIdle();
+
+        checkSampleInvalidation(aView, false);
+    }
+
+    // Unchanged: Non-minimized invalidations (bFullRow: true)
+
+    // Centered merged cells;
+    cellInvalidationHelper(pModelObj, pView, aD13, true, true);
+
+    // switch to RTL sheet
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::PAGEDOWN | 
KEY_MOD1);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::PAGEDOWN | 
KEY_MOD1);
+    Scheduler::ProcessEventsToIdle();
+
+    cellInvalidationHelper(pModelObj, pView, aA8, true, true);
+    cellInvalidationHelper(pModelObj, pView, aD4, true, true);
+    // Delete Text
+    cellInvalidationHelper(pModelObj, pView, aA8, false, true);
+}
+
 // That we don't end up with two views on different zooms that invalidate 
different
 // rectangles, each should invalidate the same rectangle
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testCellInvalidationDocWithExistingZoom)
@@ -3431,15 +3546,10 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testCellInvalidationDocWithExistingZo
     // That they don't exactly match doesn't matter, we're not checking 
rounding issues,
     // what matters is that they are not utterly different rectangles
     // Without fix result is originally:
-    // Comparing invalidation rects Left expected 278 actual 1213 Tolerance 50
-    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(
-                                          aView1.m_aInvalidations[0].TopLeft(),
-                                          aView2.m_aInvalidations[0].TopLeft(),
-                                          100);
-    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(
-                                          
aView1.m_aInvalidations[0].BottomLeft(),
-                                          
aView2.m_aInvalidations[0].BottomLeft(),
-                                          100);
+    // Comparing invalidation rectangles Width expected 6214742 actual 
26716502 Tolerance 50
+    CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aView2.m_aInvalidations[0],
+                                                  aView1.m_aInvalidations[0],
+                                                  50);
 }
 
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testStatusBarLocale)
diff --git a/sc/source/core/tool/hints.cxx b/sc/source/core/tool/hints.cxx
index 06ac946d9b6e..a3be243750a4 100644
--- a/sc/source/core/tool/hints.cxx
+++ b/sc/source/core/tool/hints.cxx
@@ -22,9 +22,10 @@
 
 // ScPaintHint - info what has to be repainted
 
-ScPaintHint::ScPaintHint( const ScRange& rRng, PaintPartFlags nPaint ) :
+ScPaintHint::ScPaintHint( const ScRange& rRng, PaintPartFlags nPaint, 
tools::Long nMaxWidthAffectedHint ) :
     aRange( rRng ),
-    nParts( nPaint )
+    nParts( nPaint ),
+    nWidthAffectedHint(nMaxWidthAffectedHint)
 {
 }
 
diff --git a/sc/source/ui/docshell/docfunc.cxx 
b/sc/source/ui/docshell/docfunc.cxx
index 1a8d902bea19..21aee819174b 100644
--- a/sc/source/ui/docshell/docfunc.cxx
+++ b/sc/source/ui/docshell/docfunc.cxx
@@ -671,6 +671,26 @@ bool ScDocFunc::DeleteContents(
     return true;
 }
 
+tools::Long ScDocShell::GetPixelWidthHint(const ScAddress& rPos)
+{
+    ScViewData* pViewData = GetViewData();
+    if (!pViewData)
+        return -1;
+
+    ScSizeDeviceProvider aProv(this);
+    OutputDevice* pDev = aProv.GetDevice();         // has pixel MapMode
+    double nPPTX = aProv.GetPPTX();
+    double nPPTY = aProv.GetPPTY();
+
+    ScDocument& rDoc = GetDocument();
+    Fraction aInvX(pViewData->GetZoomX().GetDenominator(),
+                   pViewData->GetZoomX().GetNumerator());
+    Fraction aInvY(pViewData->GetZoomY().GetDenominator(),
+                   pViewData->GetZoomY().GetNumerator());
+    return rDoc.GetNeededSize(rPos.Col(), rPos.Row(), rPos.Tab(), pDev,
+                              nPPTX, nPPTY, aInvX, aInvY, true /*bWidth*/);
+}
+
 bool ScDocFunc::DeleteCell(
     const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, 
bool bRecord, bool bApi )
 {
@@ -719,6 +739,7 @@ bool ScDocFunc::DeleteCell(
         pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, rPos);
     }
 
+    tools::Long nBefore(rDocShell.GetPixelWidthHint(rPos));
     rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, 
nFlags);
 
     if (bRecord)
@@ -731,7 +752,7 @@ bool ScDocFunc::DeleteCell(
     if (!AdjustRowHeight(rPos, true, bApi))
         rDocShell.PostPaint(
             rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), 
rPos.Tab(),
-            PaintPartFlags::Grid, nExtFlags);
+            PaintPartFlags::Grid, nExtFlags, nBefore);
 
     aModificator.SetDocumentModified();
 
@@ -833,7 +854,9 @@ bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const 
ScAddress& rPos, con
         aOldValues.push_back(aOldValue);
     }
 
+    tools::Long nBefore(rDocShell.GetPixelWidthHint(rPos));
     o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText 
);
+    tools::Long nAfter(rDocShell.GetPixelWidthHint(rPos));
 
     if (bUndo)
     {
@@ -845,7 +868,7 @@ bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const 
ScAddress& rPos, con
     if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), 
HasAttrFlags::NeedHeight ) )
         AdjustRowHeight( ScRange(rPos), true, bApi );
 
-    rDocShell.PostPaintCell( rPos );
+    rDocShell.PostPaintCell( rPos, std::max(nBefore, nAfter) );
     aModificator.SetDocumentModified();
 
     // notify input handler here the same way as in PutCell
diff --git a/sc/source/ui/docshell/docsh3.cxx b/sc/source/ui/docshell/docsh3.cxx
index 96546d11a5fe..39b74be3f466 100644
--- a/sc/source/ui/docshell/docsh3.cxx
+++ b/sc/source/ui/docshell/docsh3.cxx
@@ -100,13 +100,13 @@ void ScDocShell::PostDataChanged()
 
 void ScDocShell::PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
                             SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, 
PaintPartFlags nPart,
-                            sal_uInt16 nExtFlags )
+                            sal_uInt16 nExtFlags, tools::Long 
nMaxWidthAffectedHint )
 {
     ScRange aRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab);
-    PostPaint(aRange, nPart, nExtFlags);
+    PostPaint(aRange, nPart, nExtFlags, nMaxWidthAffectedHint);
 }
 
-void ScDocShell::PostPaint( const ScRangeList& rRanges, PaintPartFlags nPart, 
sal_uInt16 nExtFlags )
+void ScDocShell::PostPaint( const ScRangeList& rRanges, PaintPartFlags nPart, 
sal_uInt16 nExtFlags, tools::Long nMaxWidthAffectedHint )
 {
     ScRangeList aPaintRanges;
     std::set<SCTAB> aTabsInvalidated;
@@ -118,9 +118,17 @@ void ScDocShell::PostPaint( const ScRangeList& rRanges, 
PaintPartFlags nPart, sa
         SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
         SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = std::min<SCTAB>(nMaxTab, 
rRange.aEnd.Tab());
 
-        if (!m_pDocument->ValidCol(nCol1)) nCol1 = m_pDocument->MaxCol();
+        if (!m_pDocument->ValidCol(nCol1))
+        {
+            nMaxWidthAffectedHint = -1; // Hint no longer valid
+            nCol1 = m_pDocument->MaxCol();
+        }
         if (!m_pDocument->ValidRow(nRow1)) nRow1 = m_pDocument->MaxRow();
-        if (!m_pDocument->ValidCol(nCol2)) nCol2 = m_pDocument->MaxCol();
+        if (!m_pDocument->ValidCol(nCol2))
+        {
+            nMaxWidthAffectedHint = -1; // Hint no longer valid
+            nCol2 = m_pDocument->MaxCol();
+        }
         if (!m_pDocument->ValidRow(nRow2)) nRow2 = m_pDocument->MaxRow();
 
         if ( m_pPaintLockData )
@@ -143,8 +151,16 @@ void ScDocShell::PostPaint( const ScRangeList& rRanges, 
PaintPartFlags nPart, sa
         if (nExtFlags & SC_PF_LINES)            // respect space for lines
         {
                                                 //! check for hidden 
columns/rows!
-            if (nCol1>0) --nCol1;
-            if (nCol2<m_pDocument->MaxCol()) ++nCol2;
+            if (nCol1 > 0)
+            {
+                nMaxWidthAffectedHint = -1; // Hint no longer valid
+                --nCol1;
+            }
+            if (nCol2 < m_pDocument->MaxCol())
+            {
+                nMaxWidthAffectedHint = -1; // Hint no longer valid
+                ++nCol2;
+            }
             if (nRow1>0) --nRow1;
             if (nRow2<m_pDocument->MaxRow()) ++nRow2;
         }
@@ -166,6 +182,7 @@ void ScDocShell::PostPaint( const ScRangeList& rRanges, 
PaintPartFlags nPart, sa
             {
                 nCol1 = 0;
                 nCol2 = m_pDocument->MaxCol();
+                nMaxWidthAffectedHint = -1; // Hint no longer valid
             }
         }
         aPaintRanges.push_back(ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, 
nTab2));
@@ -173,7 +190,7 @@ void ScDocShell::PostPaint( const ScRangeList& rRanges, 
PaintPartFlags nPart, sa
             aTabsInvalidated.insert(nTabNum);
     }
 
-    Broadcast(ScPaintHint(aPaintRanges.Combine(), nPart));
+    Broadcast(ScPaintHint(aPaintRanges.Combine(), nPart, 
nMaxWidthAffectedHint));
 
     // LOK: we are supposed to update the row / columns headers (and actually
     // the document size too - cell size affects that, obviously)
@@ -190,14 +207,14 @@ void ScDocShell::PostPaintGridAll()
     PostPaint( 0,0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(),MAXTAB, 
PaintPartFlags::Grid );
 }
 
-void ScDocShell::PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab )
+void ScDocShell::PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab, 
tools::Long nMaxWidthAffectedHint )
 {
-    PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, 
SC_PF_TESTMERGE );
+    PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid, 
SC_PF_TESTMERGE, nMaxWidthAffectedHint );
 }
 
-void ScDocShell::PostPaintCell( const ScAddress& rPos )
+void ScDocShell::PostPaintCell( const ScAddress& rPos, tools::Long 
nMaxWidthAffectedHint )
 {
-    PostPaintCell( rPos.Col(), rPos.Row(), rPos.Tab() );
+    PostPaintCell( rPos.Col(), rPos.Row(), rPos.Tab(), nMaxWidthAffectedHint );
 }
 
 void ScDocShell::PostPaintExtras()
diff --git a/sc/source/ui/inc/docsh.hxx b/sc/source/ui/inc/docsh.hxx
index eb9a3450ad22..d476ce15222e 100644
--- a/sc/source/ui/inc/docsh.hxx
+++ b/sc/source/ui/inc/docsh.hxx
@@ -313,13 +313,16 @@ public:
 
     void            PostEditView( ScEditEngineDefaulter* pEditEngine, const 
ScAddress& rCursorPos );
 
+    tools::Long     GetPixelWidthHint(const ScAddress& rPos);
+
     void            PostPaint( SCCOL nStartCol, SCROW nStartRow, SCTAB 
nStartTab,
                             SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, 
PaintPartFlags nPart,
-                            sal_uInt16 nExtFlags = 0 );
-    void            PostPaint( const ScRangeList& rRanges, PaintPartFlags 
nPart, sal_uInt16 nExtFlags = 0 );
+                            sal_uInt16 nExtFlags = 0, tools::Long 
nMaxWidthAffectedHint = -1 );
+    void            PostPaint( const ScRangeList& rRanges, PaintPartFlags 
nPart, sal_uInt16 nExtFlags = 0,
+                               tools::Long nMaxWidthAffectedHint = -1 );
 
-    void            PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab );
-    void            PostPaintCell( const ScAddress& rPos );
+    void            PostPaintCell( SCCOL nCol, SCROW nRow, SCTAB nTab, 
tools::Long nMaxWidthAffectedHint = -1);
+    void            PostPaintCell( const ScAddress& rPos, tools::Long 
nMaxWidthAffectedHint = -1);
     void            PostPaintGridAll();
     void            PostPaintExtras();
 
diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx
index 9c6a2138e6d7..93f1b60e5759 100644
--- a/sc/source/ui/inc/tabview.hxx
+++ b/sc/source/ui/inc/tabview.hxx
@@ -483,7 +483,8 @@ public:
                                     // Drawing
 
     void            PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL 
nEndCol, SCROW nEndRow,
-                                        ScUpdateMode eMode = ScUpdateMode::All 
);
+                               ScUpdateMode eMode = ScUpdateMode::All,
+                               tools::Long nMaxWidthAffectedHint = -1 );
 
     void            PaintGrid();
 
diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx
index 444b78b20b64..0d4086bfb4a2 100644
--- a/sc/source/ui/view/tabview3.cxx
+++ b/sc/source/ui/view/tabview3.cxx
@@ -2364,7 +2364,7 @@ void ScTabView::UpdateFormulas(SCCOL nStartCol, SCROW 
nStartRow, SCCOL nEndCol,
 //  PaintArea - repaint block
 
 void ScTabView::PaintArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, 
SCROW nEndRow,
-                            ScUpdateMode eMode )
+                            ScUpdateMode eMode, tools::Long 
nMaxWidthAffectedHint )
 {
     SCCOL nCol1;
     SCROW nRow1;
@@ -2442,20 +2442,32 @@ void ScTabView::PaintArea( SCCOL nStartCol, SCROW 
nStartRow, SCCOL nEndCol, SCRO
 
         if ( eMode == ScUpdateMode::All )
         {
-            if (bIsTiledRendering)
+            if (nMaxWidthAffectedHint != -1)
             {
-                // When a cell content is deleted we have no clue about
-                // the width of the embedded text.
-                // Anyway, clients will ask only for tiles that overlaps
-                // the visible area.
-                // Remember that wsd expects int and that aEnd.X() is
-                // in pixels and will be converted in twips, before performing
-                // the lok callback, so we need to avoid that an overflow 
occurs.
-                aEnd.setX( std::numeric_limits<int>::max() / 1000 );
+                // If we know the max text width affected then just invalidate
+                // the max of the cell width and hint of affected cell width
+                // (where affected with is in terms of max width of optimal 
cell
+                // width for before/after change)
+                tools::Long nCellWidth = std::abs(aEnd.X() - aStart.X());
+                aEnd.setX(aStart.getX() + std::max(nCellWidth, 
nMaxWidthAffectedHint) * nLayoutSign);
             }
             else
             {
-                aEnd.setX( bLayoutRTL ? 0 : 
pGridWin[i]->GetOutputSizePixel().Width() );
+                if (bIsTiledRendering)
+                {
+                    // When a cell content is deleted we have no clue about
+                    // the width of the embedded text.
+                    // Anyway, clients will ask only for tiles that overlaps
+                    // the visible area.
+                    // Remember that wsd expects int and that aEnd.X() is
+                    // in pixels and will be converted in twips, before 
performing
+                    // the lok callback, so we need to avoid that an overflow 
occurs.
+                    aEnd.setX( std::numeric_limits<int>::max() / 1000 );
+                }
+                else
+                {
+                    aEnd.setX( bLayoutRTL ? 0 : 
pGridWin[i]->GetOutputSizePixel().Width() );
+                }
             }
         }
         aEnd.AdjustX( -nLayoutSign );
diff --git a/sc/source/ui/view/tabvwsh5.cxx b/sc/source/ui/view/tabvwsh5.cxx
index fab96304f39f..0cd44d2caf7e 100644
--- a/sc/source/ui/view/tabvwsh5.cxx
+++ b/sc/source/ui/view/tabvwsh5.cxx
@@ -57,12 +57,15 @@ void ScTabViewShell::Notify( SfxBroadcaster& rBC, const 
SfxHint& rHint )
 
             if (nParts & PaintPartFlags::Size)
                 RepeatResize();                     //! InvalidateBorder ???
+            const tools::Long nWidthAffectedHint = 
pPaintHint->GetMaxWidthAffectedHint();
             if (nParts & PaintPartFlags::Grid)
                 PaintArea( pPaintHint->GetStartCol(), 
pPaintHint->GetStartRow(),
-                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow() );
+                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
+                           ScUpdateMode::All, nWidthAffectedHint );
             if (nParts & PaintPartFlags::Marks)
                 PaintArea( pPaintHint->GetStartCol(), 
pPaintHint->GetStartRow(),
-                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(), 
ScUpdateMode::Marks );
+                           pPaintHint->GetEndCol(), pPaintHint->GetEndRow(),
+                           ScUpdateMode::Marks, nWidthAffectedHint );
             if (nParts & PaintPartFlags::Left)
                 PaintLeftArea( pPaintHint->GetStartRow(), 
pPaintHint->GetEndRow() );
             if (nParts & PaintPartFlags::Top)
diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx
index 7a6403237b89..fb0e469d5205 100644
--- a/sc/source/ui/view/viewfun3.cxx
+++ b/sc/source/ui/view/viewfun3.cxx
@@ -1262,6 +1262,11 @@ bool ScViewFunc::PasteFromClip( InsertDeleteFlags 
nFlags, ScDocument* pClipDoc,
         }
     }
 
+    const bool bSingleCellBefore = nStartCol == nEndCol &&
+                                   nStartRow == nEndRow &&
+                                   nStartTab == nEndTab;
+    tools::Long nBeforeHint(bSingleCellBefore ? 
pDocSh->GetPixelWidthHint(ScAddress(nStartCol, nStartRow, nStartTab)) : -1);
+
     sal_uInt16 nExtFlags = 0;
     pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab,
                                        nEndCol,   nEndRow,   nEndTab );     // 
content before the change
@@ -1437,9 +1442,20 @@ bool ScViewFunc::PasteFromClip( InsertDeleteFlags 
nFlags, ScDocument* pClipDoc,
         nPaint |= PaintPartFlags::Left;
         nUndoEndRow = rDoc.MaxRow();               // just for drawing !
     }
+
+    tools::Long nMaxWidthAffectedHint = -1;
+    const bool bSingleCellAfter = nStartCol == nUndoEndCol &&
+                                  nStartRow == nUndoEndRow &&
+                                  nStartTab == nEndTab;
+    if (bSingleCellBefore && bSingleCellAfter)
+    {
+        tools::Long nAfterHint(pDocSh->GetPixelWidthHint(ScAddress(nStartCol, 
nStartRow, nStartTab)));
+        nMaxWidthAffectedHint = std::max(nBeforeHint, nAfterHint);
+    }
+
     pDocSh->PostPaint(
         ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, 
nEndTab),
-        nPaint, nExtFlags);
+        nPaint, nExtFlags, nMaxWidthAffectedHint);
     // AdjustBlockHeight has already been called above
 
     aModificator.SetDocumentModified();
commit d8af51f9c521dca999c22a025e625f09002907b6
Author:     Caolán McNamara <caolan.mcnam...@collabora.com>
AuthorDate: Wed Jan 17 21:09:41 2024 +0000
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Tue Jan 23 22:11:20 2024 +0100

    calc doc with saved zoom in settings causes disjoint invalidations...
    
    in multiple views, while the invalidations rectangles should be reported
    at the same place from each view.
    
    on load, ScTabView::SetZoom gets called with the zoom stored in the
    document settings, which both calls sets Zoom on the ViewData, and then
    calls ZoomChanged, which syncs the GridWindows MapMode from the ViewData
    derived GetDrawMapMode().
    
    Later lok sets zoom via setClientArea which leaves the GridWindows
    MapMode untouched and out of sync with the newly changed ViewData
    MapMode.
    
    Typically then, on e.g. on deleting text in one view then
    ScViewFunc::DeleteContents or similar is called which calls
    ScTabView::UpdateCopySourceOverlay which calls
    ScGridWindow::UpdateCopySourceOverlay and that sets the GridWindow
    MapMode to the DrawMapMode but then *for lokit* returns early (among a
    few other unlikely early return cases) while every other similar func
    restores the orig GridWindow mode before returning. So the view which
    is used to make the change ends up with GridWindows synced to the
    ViewData MapMode, which looks like accident. While the other view
    remains with GridWindows with MapModes unsynced with that views
    ViewData MapMode.
    
    So on invalidate, the view that was used to make the change has
    GridWindows with MapModes that report the correct rectangle, while the
    other unsynced view will report an incorrect rectangle, until something
    happens in that view to get it to exec UpdateCopySourceOverlay and get
    synced.
    
    Here add the sync to ScModelObj::setClientZoom so the two MapModes
    remain synced once that is called.
    
    Change-Id: I2da59f50ae2b0e3ea6b7ef8b54debdab1ee98266
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162312
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Michael Meeks <michael.me...@collabora.com>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162322
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    (cherry picked from commit 7edfb1b85cef5e5435cd2bf46e1b91b68f1e6427)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/162482

diff --git a/include/test/unoapi_test.hxx b/include/test/unoapi_test.hxx
index 25945832963b..a28784543c9f 100644
--- a/include/test/unoapi_test.hxx
+++ b/include/test/unoapi_test.hxx
@@ -103,6 +103,18 @@ inline void assertRectangleEqual(const tools::Rectangle& 
rExpected, const tools:
 #define CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aExpected, aActual, 
aTolerance)              \
     assertRectangleEqual(aExpected, aActual, aTolerance, CPPUNIT_SOURCELINE())
 
+inline void assertPointEqual(const Point& rExpected, const Point& rActual,
+                             const sal_Int32 nTolerance, const 
CppUnit::SourceLine& rSourceLine)
+{
+    CPPUNIT_NS::assertDoubleEquals(rExpected.X(), rActual.X(), nTolerance, 
rSourceLine,
+                                   "different X");
+    CPPUNIT_NS::assertDoubleEquals(rExpected.Y(), rActual.Y(), nTolerance, 
rSourceLine,
+                                   "different Y");
+}
+
+#define CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aExpected, aActual, 
aTolerance)                  \
+    assertPointEqual(aExpected, aActual, aTolerance, CPPUNIT_SOURCELINE())
+
 #endif // INCLUDED_TEST_UNOAPI_TEST_HXX
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/qa/unit/helper/qahelper.hxx b/sc/qa/unit/helper/qahelper.hxx
index 4b95451c96d1..3bb12e701c70 100644
--- a/sc/qa/unit/helper/qahelper.hxx
+++ b/sc/qa/unit/helper/qahelper.hxx
@@ -201,15 +201,4 @@ private:
 #define ASSERT_DOUBLES_EQUAL_MESSAGE( message, expected, result )   \
     CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE( (message), (expected), (result), 
1e-14 )
 
-inline void assertPointEqual(
-    const Point& rExpected, const Point& rActual, const sal_Int32 nTolerance,
-    const CppUnit::SourceLine& rSourceLine )
-{
-    CPPUNIT_NS::assertDoubleEquals( rExpected.X(), rActual.X(), nTolerance, 
rSourceLine, "different X" );
-    CPPUNIT_NS::assertDoubleEquals( rExpected.Y(), rActual.Y(), nTolerance, 
rSourceLine, "different Y" );
-}
-
-#define CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(aExpected, aActual, 
aTolerance) \
-        assertPointEqual( aExpected, aActual, aTolerance, CPPUNIT_SOURCELINE() 
)
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git 
a/sc/qa/unit/tiledrendering/data/cell-invalidations-200zoom-settings.ods 
b/sc/qa/unit/tiledrendering/data/cell-invalidations-200zoom-settings.ods
new file mode 100644
index 000000000000..741441800e55
Binary files /dev/null and 
b/sc/qa/unit/tiledrendering/data/cell-invalidations-200zoom-settings.ods differ
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx 
b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index b2b5ea6ac21d..d645d6f28dbc 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -7,6 +7,7 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <test/cppunitasserthelper.hxx>
 #include <test/unoapixml_test.hxx>
 #include <test/helper/transferable.hxx>
 #include <cppunit/tools/StringHelper.h>
@@ -98,7 +99,6 @@ public:
     virtual void setUp() override;
     virtual void tearDown() override;
 
-protected:
     ScModelObj* createDoc(const char* pName);
     void setupLibreOfficeKitViewCallback(SfxViewShell* pViewShell);
     static void callback(int nType, const char* pPayload, void* pData);
@@ -3281,6 +3281,167 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testOpenURL)
     CPPUNIT_ASSERT(!aView2.m_aHyperlinkClicked.isEmpty());
 }
 
+CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testInvalidateForSplitPanes)
+{
+    comphelper::LibreOfficeKit::setCompatFlag(
+        comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+    ScModelObj* pModelObj = createDoc("split.ods");
+    CPPUNIT_ASSERT(pModelObj);
+    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pView);
+
+    // view
+    ViewCallback aView;
+
+    // move way over to the right where BP:20 exists, enough so that rows A 
and B
+    // would scroll off the page and not be visible, if they were not frozen
+    pModelObj->setClientVisibleArea(tools::Rectangle(73050, 0, 94019, 7034));
+    Scheduler::ProcessEventsToIdle();
+
+    ScAddress aBP20(67, 19, 0); // BP:20
+
+    pView->SetCursor(aBP20.Col(), aBP20.Row());
+    Scheduler::ProcessEventsToIdle();
+
+    aView.m_bInvalidateTiles = false;
+    aView.m_aInvalidations.clear();
+
+    lcl_typeCharsInCell("X", aBP20.Col(), aBP20.Row(), pView, pModelObj); // 
Type 'X' in A1
+
+    CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
+
+    // missing before fix
+    tools::Rectangle aTopLeftPane(0, 500, 3817, 742);
+    bool bFoundTopLeftPane =
+        std::find(aView.m_aInvalidations.begin(), 
aView.m_aInvalidations.end(), aTopLeftPane) != aView.m_aInvalidations.end();
+    CPPUNIT_ASSERT_MESSAGE("The cell visible in the top left pane should be 
redrawn", bFoundTopLeftPane);
+
+    // missing before fix
+    tools::Rectangle aBottomLeftPane(0, 500, 3817, 3242);
+    bool bFoundBottomLeftPane =
+        std::find(aView.m_aInvalidations.begin(), 
aView.m_aInvalidations.end(), aBottomLeftPane) != aView.m_aInvalidations.end();
+    CPPUNIT_ASSERT_MESSAGE("The cell visible in the bottom left pane should be 
redrawn", bFoundBottomLeftPane);
+}
+
+// Saving shouldn't trigger an invalidation
+CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testNoInvalidateOnSave)
+{
+    comphelper::LibreOfficeKit::setCompatFlag(
+        comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
+
+    loadFromFile(u"invalidate-on-save.ods");
+
+    // .uno:Save modifies the original file, make a copy first
+    saveAndReload("calc8");
+    ScModelObj* pModelObj = 
comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
+    CPPUNIT_ASSERT(pModelObj);
+    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+
+    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pView);
+
+    Scheduler::ProcessEventsToIdle();
+
+    // track invalidations
+    ViewCallback aView;
+
+    uno::Sequence<beans::PropertyValue> aArgs;
+    dispatchCommand(mxComponent, ".uno:Save", aArgs);
+
+    Scheduler::ProcessEventsToIdle();
+
+    CPPUNIT_ASSERT(!aView.m_bInvalidateTiles);
+}
+
+// That we don't end up with two views on different zooms that invalidate 
different
+// rectangles, each should invalidate the same rectangle
+CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testCellInvalidationDocWithExistingZoom)
+{
+    ScAddress aB7(1, 6, 0);
+    ScopedVclPtrInstance<VirtualDevice> xDevice(DeviceFormat::WITHOUT_ALPHA);
+
+    ScModelObj* pModelObj = 
createDoc("cell-invalidations-200zoom-settings.ods");
+    CPPUNIT_ASSERT(pModelObj);
+    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pView);
+
+    // Set View #1 to initial 100% and generate a paint
+    pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 19845, 6405));
+    pModelObj->setClientZoom(256, 256, 1536, 1536);
+    pModelObj->paintTile(*xDevice, 3328, 512, 0, 0, 19968, 3072);
+
+    Scheduler::ProcessEventsToIdle();
+
+    int nView1 = SfxLokHelper::getView();
+    // register to track View #1 invalidations
+    ViewCallback aView1;
+
+    // Create a View #2
+    SfxLokHelper::createView();
+    int nView2 = SfxLokHelper::getView();
+    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+    // register to track View #1 invalidations
+    ViewCallback aView2;
+
+    // Set View #2 to initial 100% and generate a paint
+    pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 19845, 6405));
+    pModelObj->setClientZoom(256, 256, 1536, 1536);
+    pModelObj->paintTile(*xDevice, 3328, 512, 0, 0, 19968, 3072);
+
+    // Set View #1 to 50% zoom and generate a paint
+    SfxLokHelper::setView(nView1);
+    pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 41150, 13250));
+    pModelObj->setClientZoom(256, 256, 3185, 3185);
+    pModelObj->paintTile(*xDevice, 3328, 512, 0, 0, 41405, 6370);
+
+    Scheduler::ProcessEventsToIdle();
+
+    // Set View #2 to 200% zoom and generate a paint
+    SfxLokHelper::setView(nView2);
+    pModelObj->setClientVisibleArea(tools::Rectangle(0, 0, 9574, 3090));
+    pModelObj->setClientZoom(256, 256, 741, 741);
+    pModelObj->paintTile(*xDevice, 3328, 512, 0, 0, 19968, 3072);
+
+    Scheduler::ProcessEventsToIdle();
+    aView1.m_bInvalidateTiles = false;
+    aView1.m_aInvalidations.clear();
+    aView2.m_bInvalidateTiles = false;
+    aView2.m_aInvalidations.clear();
+
+    ScTabViewShell* pView2 = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+    CPPUNIT_ASSERT(pView2);
+    pView2->SetCursor(aB7.Col(), aB7.Row());
+
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DELETE);
+    pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DELETE);
+    Scheduler::ProcessEventsToIdle();
+
+    // The problem tested for here is with two views at different zooms then a
+    // single cell invalidation resulted in the same rectangle reported as two
+    // different invalidations rectangles of different scales. While we should
+    // get the same invalidation rectangle reported.
+    //
+    // (B7 is a good choice to use in the real world to see the effect, to both
+    // avoid getting the two rects combined into one bigger one, or to have the
+    // two separated by so much space the 2nd is off-screen and not seen
+    CPPUNIT_ASSERT_EQUAL(size_t(1), aView1.m_aInvalidations.size());
+    CPPUNIT_ASSERT_EQUAL(size_t(1), aView2.m_aInvalidations.size());
+
+    // That they don't exactly match doesn't matter, we're not checking 
rounding issues,
+    // what matters is that they are not utterly different rectangles
+    // Without fix result is originally:
+    // Comparing invalidation rects Left expected 278 actual 1213 Tolerance 50
+    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(
+                                          aView1.m_aInvalidations[0].TopLeft(),
+                                          aView2.m_aInvalidations[0].TopLeft(),
+                                          100);
+    CPPUNIT_ASSERT_POINT_EQUAL_WITH_TOLERANCE(
+                                          
aView1.m_aInvalidations[0].BottomLeft(),
+                                          
aView2.m_aInvalidations[0].BottomLeft(),
+                                          100);
+}
+
 CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testStatusBarLocale)
 {
     // Given 2 views, the second's locale is set to German:
@@ -3435,76 +3596,6 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, 
testLongFirstColumnMouseClick)
     CPPUNIT_ASSERT_EQUAL(SCROW(0), ScDocShell::GetViewData()->GetCurY());
 }
 
-CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testInvalidateForSplitPanes)
-{
-    comphelper::LibreOfficeKit::setCompatFlag(
-        comphelper::LibreOfficeKit::Compat::scPrintTwipsMsgs);
-
-    ScModelObj* pModelObj = createDoc("split.ods");
-    CPPUNIT_ASSERT(pModelObj);
-    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
-    CPPUNIT_ASSERT(pView);
-
-    // view
-    ViewCallback aView;
-
-    // move way over to the right where BP:20 exists, enough so that rows A 
and B
-    // would scroll off the page and not be visible, if they were not frozen
-    pModelObj->setClientVisibleArea(tools::Rectangle(73050, 0, 94019, 7034));
-    Scheduler::ProcessEventsToIdle();
-
-    ScAddress aBP20(67, 19, 0); // BP:20
-
-    pView->SetCursor(aBP20.Col(), aBP20.Row());
-    Scheduler::ProcessEventsToIdle();
-
-    aView.m_bInvalidateTiles = false;
-    aView.m_aInvalidations.clear();
-
-    lcl_typeCharsInCell("X", aBP20.Col(), aBP20.Row(), pView, pModelObj); // 
Type 'X' in A1
-
-    CPPUNIT_ASSERT(aView.m_bInvalidateTiles);
-
-    // missing before fix
-    tools::Rectangle aTopLeftPane(0, 500, 3817, 742);
-    bool bFoundTopLeftPane =
-        std::find(aView.m_aInvalidations.begin(), 
aView.m_aInvalidations.end(), aTopLeftPane) != aView.m_aInvalidations.end();
-    CPPUNIT_ASSERT_MESSAGE("The cell visible in the top left pane should be 
redrawn", bFoundTopLeftPane);
-
-    // missing before fix
-    tools::Rectangle aBottomLeftPane(0, 500, 3817, 3242);
-    bool bFoundBottomLeftPane =
-        std::find(aView.m_aInvalidations.begin(), 
aView.m_aInvalidations.end(), aBottomLeftPane) != aView.m_aInvalidations.end();
-    CPPUNIT_ASSERT_MESSAGE("The cell visible in the bottom left pane should be 
redrawn", bFoundBottomLeftPane);
-}
-
-// Saving shouldn't trigger an invalidation
-CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testNoInvalidateOnSave)
-{
-    loadFromFile(u"invalidate-on-save.ods");
-
-    // .uno:Save modifies the original file, make a copy first
-    saveAndReload("calc8");
-    ScModelObj* pModelObj = 
comphelper::getFromUnoTunnel<ScModelObj>(mxComponent);
-    CPPUNIT_ASSERT(pModelObj);
-    
pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
-
-    ScTabViewShell* pView = 
dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
-    CPPUNIT_ASSERT(pView);
-
-    Scheduler::ProcessEventsToIdle();
-
-    // track invalidations
-    ViewCallback aView;
-
-    uno::Sequence<beans::PropertyValue> aArgs;
-    dispatchCommand(mxComponent, ".uno:Save", aArgs);
-
-    Scheduler::ProcessEventsToIdle();
-
-    CPPUNIT_ASSERT(!aView.m_bInvalidateTiles);
-}
-
 CPPUNIT_PLUGIN_IMPLEMENT();
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx
index d825eca1f1d8..9c6a2138e6d7 100644
--- a/sc/source/ui/inc/tabview.hxx
+++ b/sc/source/ui/inc/tabview.hxx
@@ -632,6 +632,8 @@ public:
     SCROW GetLOKEndHeaderRow() const { return mnLOKEndHeaderRow; }
     SCCOL GetLOKStartHeaderCol() const { return mnLOKStartHeaderCol; }
     SCCOL GetLOKEndHeaderCol() const { return mnLOKEndHeaderCol; }
+
+    void SyncGridWindowMapModeFromDrawMapMode();
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index da265c97a1b0..f1599ed413d5 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -1061,6 +1061,8 @@ void ScModelObj::setClientZoom(int nTilePixelWidth_, int 
nTilePixelHeight_, int
         return;
 
     pViewData->SetZoom(newZoomX, newZoomY, true);
+    if (ScTabViewShell* pViewShell = pViewData->GetViewShell())
+        pViewShell->SyncGridWindowMapModeFromDrawMapMode();
 
     // refresh our view's take on other view's cursors & selections
     pViewData->GetActiveWin()->updateKitOtherCursors();
diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
index 71a37ddce9da..1bf2b710ba01 100644
--- a/sc/source/ui/view/tabview.cxx
+++ b/sc/source/ui/view/tabview.cxx
@@ -1719,9 +1719,7 @@ void ScTabView::DoHSplit(tools::Long nSplitPos)
 
     // Form Layer needs to know the visible part of all windows
     // that is why MapMode must already be correct here
-    for (VclPtr<ScGridWindow> & pWin : pGridWin)
-        if (pWin)
-            pWin->SetMapMode( pWin->GetDrawMapMode() );
+    SyncGridWindowMapModeFromDrawMapMode();
     SetNewVisArea();
 
     PaintGrid();
@@ -1791,9 +1789,7 @@ void ScTabView::DoVSplit(tools::Long nSplitPos)
 
     // Form Layer needs to know the visible part of all windows
     // that is why MapMode must already be correct here
-    for (VclPtr<ScGridWindow> & pWin : pGridWin)
-        if (pWin)
-            pWin->SetMapMode( pWin->GetDrawMapMode() );
+    SyncGridWindowMapModeFromDrawMapMode();
     SetNewVisArea();
 
     PaintGrid();
@@ -2211,9 +2207,7 @@ void ScTabView::FreezeSplitters( bool bFreeze, 
SplitMethod eSplitMethod, SCCOLRO
 
     // Form Layer needs to know the visible part of all windows
     // that is why MapMode must already be correct here
-    for (VclPtr<ScGridWindow> & p : pGridWin)
-        if (p)
-            p->SetMapMode( p->GetDrawMapMode() );
+    SyncGridWindowMapModeFromDrawMapMode();
     SetNewVisArea();
 
     RepeatResize(bUpdateFix);
diff --git a/sc/source/ui/view/tabview3.cxx b/sc/source/ui/view/tabview3.cxx
index 4a78aa38e710..444b78b20b64 100644
--- a/sc/source/ui/view/tabview3.cxx
+++ b/sc/source/ui/view/tabview3.cxx
@@ -2052,11 +2052,7 @@ void ScTabView::SetTabNo( SCTAB nTab, bool bNew, bool 
bExtendSelection, bool bSa
 
     // Form Layer must know the visible area of the new sheet
     // that is why MapMode must already be correct here
-    for (VclPtr<ScGridWindow> & pWin : pGridWin)
-    {
-        if (pWin)
-            pWin->SetMapMode(pWin->GetDrawMapMode());
-    }
+    SyncGridWindowMapModeFromDrawMapMode();
     SetNewVisArea();
 
     PaintGrid();
@@ -3100,6 +3096,20 @@ void ScTabView::UpdateInputLine()
     SC_MOD()->InputEnterHandler();
 }
 
+void ScTabView::SyncGridWindowMapModeFromDrawMapMode()
+{
+    // AW: Discussed with NN if there is a reason that new map mode was only 
set for one window,
+    // but is not. Setting only on one window causes the first repaint to have 
the old mapMode
+    // in three of four views, so the overlay will save the wrong content e.g. 
when zooming out.
+    // Changing to setting map mode at all windows.
+    for (VclPtr<ScGridWindow> & pWin : pGridWin)
+    {
+        if (!pWin)
+            continue;
+        pWin->SetMapMode(pWin->GetDrawMapMode());
+    }
+}
+
 void ScTabView::ZoomChanged()
 {
     ScInputHandler* pHdl = SC_MOD()->GetInputHdl(aViewData.GetViewShell());
@@ -3110,18 +3120,9 @@ void ScTabView::ZoomChanged()
 
     UpdateScrollBars();
 
-    // VisArea...
-    // AW: Discussed with NN if there is a reason that new map mode was only 
set for one window,
-    // but is not. Setting only on one window causes the first repaint to have 
the old mapMode
-    // in three of four views, so the overlay will save the wrong content e.g. 
when zooming out.
-    // Changing to setting map mode at all windows.
-
-    for (sal_uInt32 i = 0; i < 4; i++)
-    {
-        if (pGridWin[i])
-            pGridWin[i]->SetMapMode(pGridWin[i]->GetDrawMapMode());
-    }
+    SyncGridWindowMapModeFromDrawMapMode();
 
+    // VisArea...
     SetNewVisArea();
 
     InterpretVisible();     // have everything calculated before painting

Reply via email to