compilerplugins/clang/redundantfcast.cxx |    3 
 include/svx/framelink.hxx                |   11 ++
 sal/cppunittester/cppunittester.cxx      |    5 -
 sc/inc/attarray.hxx                      |    4 
 sc/inc/column.hxx                        |   17 ++-
 sc/inc/dociter.hxx                       |    4 
 sc/inc/document.hxx                      |   14 +--
 sc/inc/documentimport.hxx                |    9 +-
 sc/inc/fillinfo.hxx                      |    2 
 sc/inc/table.hxx                         |   14 +--
 sc/qa/unit/data/xlsx/tdf58243.xlsx       |binary
 sc/qa/unit/subsequent_export_test2.cxx   |   44 +++++++++
 sc/qa/unit/ucalc.cxx                     |  138 +++++++++++++++++++++++++++++--
 sc/source/core/data/attarray.cxx         |    9 ++
 sc/source/core/data/column3.cxx          |    2 
 sc/source/core/data/dociter.cxx          |   82 ++++--------------
 sc/source/core/data/document.cxx         |   12 +-
 sc/source/core/data/documentimport.cxx   |   17 +--
 sc/source/core/data/table2.cxx           |   82 ++++++++++--------
 sc/source/filter/excel/xistyle.cxx       |   17 +++
 sc/source/filter/inc/sheetdatabuffer.hxx |   12 +-
 sc/source/filter/oox/sheetdatabuffer.cxx |   34 ++++++-
 sc/source/filter/xml/xmlcoli.cxx         |    1 
 sc/source/filter/xml/xmlexprt.cxx        |    8 -
 sc/source/ui/unoobj/cellsuno.cxx         |    7 -
 sc/source/ui/view/output.cxx             |   38 ++++----
 sc/source/ui/view/tabview.cxx            |    8 -
 svx/source/dialog/framelink.cxx          |    5 -
 28 files changed, 409 insertions(+), 190 deletions(-)

New commits:
commit 033fe3f340a3d47bb87099e23aaa101781932262
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Apr 12 14:33:55 2022 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:18:24 2022 +0200

    bail out of the loop when the item is found
    
    Change-Id: If0816f38eeeb3330738ab40bc8a72f1e14575c33

diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx
index 532e675ad08b..ca77bae6c093 100644
--- a/sc/source/ui/unoobj/cellsuno.cxx
+++ b/sc/source/ui/unoobj/cellsuno.cxx
@@ -8819,7 +8819,6 @@ rtl::Reference<ScCellRangeObj> 
ScCellFormatsObj::GetObjectByIndex_Impl(tools::Lo
 {
     //! access the AttrArrays directly !!!!
 
-    rtl::Reference<ScCellRangeObj> pRet;
     if (pDocShell)
     {
         ScDocument& rDoc = pDocShell->GetDocument();
@@ -8837,14 +8836,14 @@ rtl::Reference<ScCellRangeObj> 
ScCellFormatsObj::GetObjectByIndex_Impl(tools::Lo
                 ScRange aNext( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
 
                 if ( aNext.aStart == aNext.aEnd )
-                    pRet = new ScCellObj( pDocShell, aNext.aStart );
+                    return new ScCellObj( pDocShell, aNext.aStart );
                 else
-                    pRet = new ScCellRangeObj( pDocShell, aNext );
+                    return new ScCellRangeObj( pDocShell, aNext );
             }
             ++nPos;
         }
     }
-    return pRet;
+    return {};
 }
 
 // XIndexAccess
commit 1de6fcb8e43a1bdac0b49b843172a634227e6303
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Mar 25 12:42:58 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:18:15 2022 +0200

    fix ScTable::GetLastChangedCol() for unallocated columns
    
    Column flags and widths are stored separately from ScColumn data,
    and so don't depend on allocated columns count.
    
    Also rename the functions from the misleading generic name to what
    they actually do.
    
    Change-Id: If85ae80efda1d8b382fa3b559aa65be0292e25ba

diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index d9e9b22be9f6..9bf190571f56 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -2027,17 +2027,17 @@ public:
      */
     void                        SyncColRowFlags();
 
-                    /// @return  the index of the last row with any set flags 
(auto-pagebreak is ignored).
+    /// @return  the index of the last row with any set flags (auto-pagebreak 
is ignored).
     SC_DLLPUBLIC SCROW          GetLastFlaggedRow( SCTAB nTab ) const;
 
-                    /// @return  the index of the last changed column (flags 
and column width, auto pagebreak is ignored).
-    SCCOL           GetLastChangedCol( SCTAB nTab ) const;
-                    /// @return  the index of the last changed row (flags and 
row height, auto pagebreak is ignored).
-    SCROW           GetLastChangedRow( SCTAB nTab ) const;
+    /// @return  the index of the last changed column (flags and column width, 
auto pagebreak is ignored).
+    SCCOL           GetLastChangedColFlagsWidth( SCTAB nTab ) const;
+    /// @return  the index of the last changed row (flags and row height, auto 
pagebreak is ignored).
+    SCROW           GetLastChangedRowFlagsWidth( SCTAB nTab ) const;
 
-    SCCOL           GetNextDifferentChangedCol( SCTAB nTab, SCCOL nStart) 
const;
+    SCCOL           GetNextDifferentChangedColFlagsWidth( SCTAB nTab, SCCOL 
nStart) const;
 
-    SCROW           GetNextDifferentChangedRow( SCTAB nTab, SCROW nStart) 
const;
+    SCROW           GetNextDifferentChangedRowFlagsWidth( SCTAB nTab, SCROW 
nStart) const;
 
     // returns whether to export a Default style for this col or not
     // nDefault is set to one position in the current row where the Default 
style is
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 55321c6965ea..8ec17616dc1f 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -883,10 +883,10 @@ public:
                 /// @return  the index of the last row with any set flags 
(auto-pagebreak is ignored).
     SCROW      GetLastFlaggedRow() const;
 
-                /// @return  the index of the last changed column (flags and 
column width, auto pagebreak is ignored).
-    SCCOL      GetLastChangedCol() const;
-                /// @return  the index of the last changed row (flags and row 
height, auto pagebreak is ignored).
-    SCROW      GetLastChangedRow() const;
+    /// @return  the index of the last changed column (flags and column width, 
auto pagebreak is ignored).
+    SCCOL      GetLastChangedColFlagsWidth() const;
+    /// @return  the index of the last changed row (flags and row height, auto 
pagebreak is ignored).
+    SCROW      GetLastChangedRowFlagsWidth() const;
 
     bool       IsDataFiltered(SCCOL nColStart, SCROW nRowStart, SCCOL nColEnd, 
SCROW nRowEnd) const;
     bool       IsDataFiltered(const ScRange& rRange) const;
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 8323ca310298..3c9a3db2417c 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -148,6 +148,7 @@ public:
     void testHorizontalAttrIterator();
     void testIteratorsUnallocatedColumnsAttributes();
     void testIteratorsDefPattern();
+    void testLastChangedColFlagsWidth();
 
     /**
      * More direct test for cell broadcaster management, used to track formula
@@ -280,6 +281,7 @@ public:
     CPPUNIT_TEST(testHorizontalAttrIterator);
     CPPUNIT_TEST(testIteratorsUnallocatedColumnsAttributes);
     CPPUNIT_TEST(testIteratorsDefPattern);
+    CPPUNIT_TEST(testLastChangedColFlagsWidth);
     CPPUNIT_TEST(testCellBroadcaster);
     CPPUNIT_TEST(testFuncParam);
     CPPUNIT_TEST(testNamedRange);
@@ -1507,6 +1509,23 @@ void Test::testIteratorsDefPattern()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testLastChangedColFlagsWidth()
+{
+    m_pDoc->InsertTab(0, "Tab1");
+
+    constexpr SCCOL firstChangedCol = 100;
+    assert( firstChangedCol > INITIALCOLCOUNT );
+    for( SCCOL col = firstChangedCol; col <= m_pDoc->MaxCol(); ++col )
+        m_pDoc->SetColWidth( col, 0, 10 );
+
+    // That shouldn't need allocating more columns, just changing column flags.
+    CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
m_pDoc->GetAllocatedColumnsCount(0));
+    // But the flags are changed.
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->MaxCol(), 
m_pDoc->GetLastChangedColFlagsWidth(0));
+
+    m_pDoc->DeleteTab(0);
+}
+
 namespace {
 
 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const 
ScAddress& rTo)
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
index 6765e8a5849e..941b8ade54ca 100644
--- a/sc/source/core/data/document.cxx
+++ b/sc/source/core/data/document.cxx
@@ -4589,21 +4589,21 @@ SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const
     return 0;
 }
 
-SCCOL ScDocument::GetLastChangedCol( SCTAB nTab ) const
+SCCOL ScDocument::GetLastChangedColFlagsWidth( SCTAB nTab ) const
 {
     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && 
maTabs[nTab] )
-        return maTabs[nTab]->GetLastChangedCol();
+        return maTabs[nTab]->GetLastChangedColFlagsWidth();
     return 0;
 }
 
-SCROW ScDocument::GetLastChangedRow( SCTAB nTab ) const
+SCROW ScDocument::GetLastChangedRowFlagsWidth( SCTAB nTab ) const
 {
     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && 
maTabs[nTab] )
-        return maTabs[nTab]->GetLastChangedRow();
+        return maTabs[nTab]->GetLastChangedRowFlagsWidth();
     return 0;
 }
 
-SCCOL ScDocument::GetNextDifferentChangedCol( SCTAB nTab, SCCOL nStart) const
+SCCOL ScDocument::GetNextDifferentChangedColFlagsWidth( SCTAB nTab, SCCOL 
nStart) const
 {
     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && 
maTabs[nTab] )
     {
@@ -4621,7 +4621,7 @@ SCCOL ScDocument::GetNextDifferentChangedCol( SCTAB nTab, 
SCCOL nStart) const
     return 0;
 }
 
-SCROW ScDocument::GetNextDifferentChangedRow( SCTAB nTab, SCROW nStart) const
+SCROW ScDocument::GetNextDifferentChangedRowFlagsWidth( SCTAB nTab, SCROW 
nStart) const
 {
     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || 
!maTabs[nTab])
         return 0;
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 642b78f1c94d..023e853ecdc4 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -3918,22 +3918,21 @@ SCROW ScTable::GetLastFlaggedRow() const
     return nLastFound;
 }
 
-SCCOL ScTable::GetLastChangedCol() const
+SCCOL ScTable::GetLastChangedColFlagsWidth() const
 {
     if ( !mpColFlags )
         return 0;
 
     SCCOL nLastFound = 0;
-    const auto nColSize = aCol.size();
     auto colWidthIt = mpColWidth->begin() + 1;
-    for (SCCOL nCol = 1; nCol < nColSize; (++nCol < nColSize) ? ++colWidthIt : 
(void)false)
+    for (SCCOL nCol = 1; nCol <= GetDoc().MaxCol(); (++nCol <= 
GetDoc().MaxCol()) ? ++colWidthIt : (void)false)
         if ((mpColFlags->GetValue(nCol) & CRFlags::All) || (*colWidthIt != 
STD_COL_WIDTH))
             nLastFound = nCol;
 
     return nLastFound;
 }
 
-SCROW ScTable::GetLastChangedRow() const
+SCROW ScTable::GetLastChangedRowFlagsWidth() const
 {
     if ( !pRowFlags )
         return 0;
diff --git a/sc/source/filter/xml/xmlexprt.cxx 
b/sc/source/filter/xml/xmlexprt.cxx
index 7be7bd4e72f0..8e9e03fca80c 100644
--- a/sc/source/filter/xml/xmlexprt.cxx
+++ b/sc/source/filter/xml/xmlexprt.cxx
@@ -2538,7 +2538,7 @@ void ScXMLExport::collectAutoStyles()
                 uno::Reference<table::XTableColumns> 
xTableColumns(xColumnRowRange->getColumns());
                 if (xTableColumns.is())
                 {
-                    sal_Int32 
nColumns(pDoc->GetLastChangedCol(sal::static_int_cast<SCTAB>(nTable)));
+                    sal_Int32 
nColumns(pDoc->GetLastChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
                     pSharedData->SetLastColumn(nTable, nColumns);
                     table::CellRangeAddress 
aCellAddress(GetEndAddress(xTable));
                     if (aCellAddress.EndColumn > nColumns)
@@ -2560,7 +2560,7 @@ void ScXMLExport::collectAutoStyles()
                             pColumnStyles->AddFieldStyleName(nTable, nColumn, 
nIndex, bIsVisible);
                         }
                         sal_Int32 nOld(nColumn);
-                        nColumn = 
pDoc->GetNextDifferentChangedCol(sal::static_int_cast<SCTAB>(nTable), 
static_cast<SCCOL>(nColumn));
+                        nColumn = 
pDoc->GetNextDifferentChangedColFlagsWidth(sal::static_int_cast<SCTAB>(nTable), 
static_cast<SCCOL>(nColumn));
                         for (sal_Int32 i = nOld + 1; i < nColumn; ++i)
                             pColumnStyles->AddFieldStyleName(nTable, i, 
nIndex, bIsVisible);
                     }
@@ -2575,7 +2575,7 @@ void ScXMLExport::collectAutoStyles()
                 uno::Reference<table::XTableRows> 
xTableRows(xColumnRowRange->getRows());
                 if (xTableRows.is())
                 {
-                    sal_Int32 
nRows(pDoc->GetLastChangedRow(sal::static_int_cast<SCTAB>(nTable)));
+                    sal_Int32 
nRows(pDoc->GetLastChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable)));
                     pSharedData->SetLastRow(nTable, nRows);
 
                     pRowStyles->AddNewTable(nTable, pDoc->MaxRow());
@@ -2590,7 +2590,7 @@ void ScXMLExport::collectAutoStyles()
                             pRowStyles->AddFieldStyleName(nTable, nRow, 
nIndex);
                         }
                         sal_Int32 nOld(nRow);
-                        nRow = 
pDoc->GetNextDifferentChangedRow(sal::static_int_cast<SCTAB>(nTable), 
static_cast<SCROW>(nRow));
+                        nRow = 
pDoc->GetNextDifferentChangedRowFlagsWidth(sal::static_int_cast<SCTAB>(nTable), 
static_cast<SCROW>(nRow));
                         if (nRow > nOld + 1)
                             pRowStyles->AddFieldStyleName(nTable, nOld + 1, 
nIndex, nRow - 1);
                     }
commit 7fe6a3eebadd9b18e788bc6547fd58fcb25afd62
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Wed Mar 23 12:26:45 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:18:06 2022 +0200

    forcepoint#81 fix array size
    
    A mistake from 694148a9898b47d749588f9a32173a9933262e29,
    the array is meant to be from column 0, so as if 'nStartCol == 0'.
    
    Change-Id: I7f2ecea6c7378d0d5530cb38314807a8f7c896c3
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131972
    Tested-by: Caolán McNamara <caol...@redhat.com>
    Reviewed-by: Caolán McNamara <caol...@redhat.com>

diff --git a/sc/inc/fillinfo.hxx b/sc/inc/fillinfo.hxx
index d6ab0bd537c6..ecd836f89ec9 100644
--- a/sc/inc/fillinfo.hxx
+++ b/sc/inc/fillinfo.hxx
@@ -221,7 +221,7 @@ struct RowInfo
         nEndCol = endCol;
 #endif
         pCellInfo = new CellInfo[ endCol - nStartCol + 1 + 2 ];
-        pBasicCellInfo = new BasicCellInfo[ endCol + 2 ];
+        pBasicCellInfo = new BasicCellInfo[ endCol + 1 + 2 ];
     }
     void                freeCellInfo()
     {
commit f142fb374756fd6ab9cba62b957e950162e059ad
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Mar 18 17:20:47 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:17:19 2022 +0200

    handle broken row ranges in excel import, #2
    
    In ad1f6e62298612f4b277708b813a219ba390f1b0 I didn't add a second
    std::unique() pass, that seems to be necessary with some broken
    documents as well
    (sc_column3_regroupformulacells_heap_buffer_overflow.sample
    from crashtesting).
    
    Change-Id: I6865085a9b77fe51246180e519bf1a2309615144

diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx 
b/sc/source/filter/oox/sheetdatabuffer.cxx
index 82fc10f35069..63b9b4f11145 100644
--- a/sc/source/filter/oox/sheetdatabuffer.cxx
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -379,9 +379,15 @@ void SheetDataBuffer::addColXfStyles()
                         // as operator< is somewhat specific (see 
StyleRowRangeComp).
                         { return !StyleRowRangeComp()(lhs,rhs) && 
!StyleRowRangeComp()(rhs,lhs); } ),
             s.end());
-        // Broken documents may have overlapping ranges that cause problems, 
re-sort again if needed.
+        // Broken documents may have overlapping ranges that cause problems, 
repeat once more.
         if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
+        {
             std::sort( s.begin(), s.end(), StyleRowRangeComp());
+            s.erase( std::unique( s.begin(), s.end(),
+                        [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+                            { return !StyleRowRangeComp()(lhs,rhs) && 
!StyleRowRangeComp()(rhs,lhs); } ),
+                s.end());
+        }
         maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( 
std::move( s ));
     }
 }
commit e7ca47af1e40d488813c48a3e425e4ad462ab891
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Mar 15 21:48:34 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:17:10 2022 +0200

    handle broken row ranges in excel import
    
    LIBREOFFICE-39G76603.xlsx from crashtesting has broken data cause
    assertion failures about the ranges being sorted (because the ranges
    overlap and StyleRowRangeComp needs and assumes that they do not).
    Changing the std::unique() call to remove ranges that compare equal
    (which is not the same as being equal, due to how StyleRowRangeComp
    compares) and sorting again seems to drop all problematic ranges.
    (I'm not adding the problematic file to tests as loading it takes
    a lot of time.)
    
    Change-Id: If6ac94959a649a641d02fd703d33edddbdf0fb85
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131637
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/source/filter/inc/sheetdatabuffer.hxx 
b/sc/source/filter/inc/sheetdatabuffer.hxx
index 8eee9f7b79b0..940da26416fe 100644
--- a/sc/source/filter/inc/sheetdatabuffer.hxx
+++ b/sc/source/filter/inc/sheetdatabuffer.hxx
@@ -207,13 +207,6 @@ private:
             return lhs.mnEndRow<rhs.mnStartRow;
         }
     };
-    struct StyleRowRangeCompEqual
-    {
-        bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) 
const
-        {
-            return lhs.mnStartRow==rhs.mnStartRow && lhs.mnEndRow == 
rhs.mnEndRow;
-        }
-    };
     typedef ::o3tl::sorted_vector< RowRangeStyle, StyleRowRangeComp > 
RowStyles;
     typedef ::std::map< sal_Int32, RowStyles > ColStyles;
     typedef ::std::vector< RowRangeStyle > TmpRowStyles;
diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx 
b/sc/source/filter/oox/sheetdatabuffer.cxx
index 259e3190107b..82fc10f35069 100644
--- a/sc/source/filter/oox/sheetdatabuffer.cxx
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -373,7 +373,15 @@ void SheetDataBuffer::addColXfStyles()
     {
         TmpRowStyles& s = rowStyles.second;
         std::sort( s.begin(), s.end(), StyleRowRangeComp());
-        s.erase( std::unique( s.begin(), s.end(), StyleRowRangeCompEqual()), 
s.end());
+        s.erase( std::unique( s.begin(), s.end(),
+                    [](const RowRangeStyle& lhs, const RowRangeStyle& rhs)
+                        // Synthetize operator== from operator < . Do not 
create an actual operator==
+                        // as operator< is somewhat specific (see 
StyleRowRangeComp).
+                        { return !StyleRowRangeComp()(lhs,rhs) && 
!StyleRowRangeComp()(rhs,lhs); } ),
+            s.end());
+        // Broken documents may have overlapping ranges that cause problems, 
re-sort again if needed.
+        if(!std::is_sorted(s.begin(), s.end(), StyleRowRangeComp()))
+            std::sort( s.begin(), s.end(), StyleRowRangeComp());
         maStylesPerColumn[ rowStyles.first ].insert_sorted_unique_vector( 
std::move( s ));
     }
 }
commit 6338a0a44d3c9eeac26419aecb866682a41eed52
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Mar 15 20:03:50 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:17:02 2022 +0200

    fix comparison operators in Excel export, #2
    
    It turns out that the end-vs-start comparison was intentional and
    the file causing problems is broken, so more or less revert
    83d599fd7c530d14f70ac60bd673b66640191bf7, and I'll handle
    the problematic file in another commit.
    
    Change-Id: I5c7538a7c3eeea9c5fd1661e1a9a2c3370b799c9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131636
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/qa/unit/data/xlsx/tdf58243.xlsx 
b/sc/qa/unit/data/xlsx/tdf58243.xlsx
new file mode 100644
index 000000000000..f95e13b4b6db
Binary files /dev/null and b/sc/qa/unit/data/xlsx/tdf58243.xlsx differ
diff --git a/sc/qa/unit/subsequent_export_test2.cxx 
b/sc/qa/unit/subsequent_export_test2.cxx
index ad3f125db7d9..9776b1629197 100644
--- a/sc/qa/unit/subsequent_export_test2.cxx
+++ b/sc/qa/unit/subsequent_export_test2.cxx
@@ -217,6 +217,7 @@ public:
     void testTdf145059();
     void testTdf130104_XLSXIndent();
     void testWholeRowBold();
+    void testXlsxRowsOrder();
 
     CPPUNIT_TEST_SUITE(ScExportTest2);
 
@@ -333,6 +334,7 @@ public:
     CPPUNIT_TEST(testTdf145059);
     CPPUNIT_TEST(testTdf130104_XLSXIndent);
     CPPUNIT_TEST(testWholeRowBold);
+    CPPUNIT_TEST(testXlsxRowsOrder);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -3113,6 +3115,15 @@ void ScExportTest2::testWholeRowBold()
     xDocSh3->DoClose();
 }
 
+void ScExportTest2::testXlsxRowsOrder()
+{
+    ScDocShellRef xDocSh = loadDoc(u"tdf58243.", FORMAT_XLSX);
+    CPPUNIT_ASSERT(xDocSh.is());
+    // Make sure code in SheetDataBuffer doesn't assert columns/rows sorting.
+    ScBootstrapFixture::exportTo(*xDocSh, FORMAT_XLSX);
+    xDocSh->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest2);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
index 5af0e3b3609d..1b84ae4616a5 100644
--- a/sc/source/core/data/attarray.cxx
+++ b/sc/source/core/data/attarray.cxx
@@ -930,6 +930,15 @@ void ScAttrArray::SetAttrEntries(std::vector<ScAttrEntry> 
&& vNewData)
         pDocPool->Remove(*rEntry.pPattern);
 
     mvData = std::move(vNewData);
+
+#ifdef DBG_UTIL
+    SCROW lastEndRow = -1;
+    for(const auto& entry : mvData)
+    {   // Verify that the data is not corrupted.
+        assert(entry.nEndRow > lastEndRow);
+        lastEndRow = entry.nEndRow;
+    }
+#endif
 }
 
 static void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource )
diff --git a/sc/source/filter/inc/sheetdatabuffer.hxx 
b/sc/source/filter/inc/sheetdatabuffer.hxx
index 33366fde9f9e..8eee9f7b79b0 100644
--- a/sc/source/filter/inc/sheetdatabuffer.hxx
+++ b/sc/source/filter/inc/sheetdatabuffer.hxx
@@ -199,14 +199,19 @@ private:
     {
         bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) 
const
         {
-            return lhs.mnStartRow<rhs.mnStartRow;
+            // This end-vs-start comparison is needed by the lower_bound() use
+            // in SheetDataBuffer::addColXfStyleProcessRowRanges() that 
searches
+            // for partially overlapping ranges. In all other places the ranges
+            // should be non-overlapping, in which case this is the same as 
the "normal"
+            // comparison.
+            return lhs.mnEndRow<rhs.mnStartRow;
         }
     };
     struct StyleRowRangeCompEqual
     {
         bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) 
const
         {
-            return lhs.mnStartRow==rhs.mnStartRow;
+            return lhs.mnStartRow==rhs.mnStartRow && lhs.mnEndRow == 
rhs.mnEndRow;
         }
     };
     typedef ::o3tl::sorted_vector< RowRangeStyle, StyleRowRangeComp > 
RowStyles;
diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx 
b/sc/source/filter/oox/sheetdatabuffer.cxx
index 6dbec9e6bd9d..259e3190107b 100644
--- a/sc/source/filter/oox/sheetdatabuffer.cxx
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -409,8 +409,6 @@ void SheetDataBuffer::addColXfStyleProcessRowRanges()
                 RowRangeStyle aStyleRows;
                 aStyleRows.mnNumFmt.first = nXfId;
                 aStyleRows.mnNumFmt.second = -1;
-                aStyleRows.mnStartRow = rRange.mnFirst;
-                aStyleRows.mnEndRow = rRange.mnLast;
 
                 // Reset row range for each column
                 aStyleRows.mnStartRow = rRange.mnFirst;
commit e6abff6c96f5b429f8da5ff0db173cbda558c69b
Author:     Luboš Luňák <l.lu...@centrum.cz>
AuthorDate: Mon Feb 28 04:02:14 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:54 2022 +0200

    set LO_TESTNAME on startup on all platforms
    
    c0b1d1bf5701d5f94b618f70da8e863d32d97ab4 did this only for UNX
    for no apparent reason. Solves CppunitTest_sc_tablecolumnsobj
    asserting on it not being set.
    
    Change-Id: Ia9484e5c90bef18450addcf9429f9a27d575c3b4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131631
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sal/cppunittester/cppunittester.cxx 
b/sal/cppunittester/cppunittester.cxx
index 5b886ea2189d..f75caccbc143 100644
--- a/sal/cppunittester/cppunittester.cxx
+++ b/sal/cppunittester/cppunittester.cxx
@@ -301,9 +301,12 @@ public:
 
             EyecatcherListener eye;
             result.addListener(&eye);
-#ifdef UNX
+
             // set this to track down files created before first test method
             std::string lib = testlib.substr(testlib.rfind('/')+1);
+#ifdef WIN32
+            _putenv_s("LO_TESTNAME", lib.c_str());
+#else
             setenv("LO_TESTNAME", lib.c_str(), true);
 #endif
             const char* pVal = getenv("CPPUNIT_TEST_NAME");
commit 52f7a58330296fa6b2cf678c7cf0e943d8214e49
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Mar 11 17:43:41 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:46 2022 +0200

    allocate column in ScTable::SetPatternAreaCondFormat() (tdf#147894)
    
    Change-Id: Id14bb20ef5e49551cef5216b5149e3c281c04223
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131389
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index 9142ae62aba7..642b78f1c94d 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -2967,7 +2967,7 @@ void ScTable::RemoveCondFormatData( const ScRangeList& 
rRangeList, sal_uInt32 nI
 void  ScTable::SetPatternAreaCondFormat( SCCOL nCol, SCROW nStartRow, SCROW 
nEndRow,
         const ScPatternAttr& rAttr, const ScCondFormatIndexes& 
rCondFormatIndexes )
 {
-    aCol[nCol].SetPatternArea( nStartRow, nEndRow, rAttr);
+    CreateColumnIfNotExists(nCol).SetPatternArea( nStartRow, nEndRow, rAttr);
 
     for (const auto& rIndex : rCondFormatIndexes)
     {
commit 754f7e8b234d91e69b3ffc39faf2aab31bc448ac
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Mar 11 09:56:20 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:37 2022 +0200

    fix comparison operators
    
    It seems the ranges are always distinct, so the < comparison always
    worked in practice, but the == comparison added by my 6810aa937caca1
    is obviously incorrect.
    
    Change-Id: Ib7fcd36b5956901265248b34a4dc69e587cebe41
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131340
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/source/filter/inc/sheetdatabuffer.hxx 
b/sc/source/filter/inc/sheetdatabuffer.hxx
index 8002e6ee93f9..33366fde9f9e 100644
--- a/sc/source/filter/inc/sheetdatabuffer.hxx
+++ b/sc/source/filter/inc/sheetdatabuffer.hxx
@@ -199,14 +199,14 @@ private:
     {
         bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) 
const
         {
-            return lhs.mnEndRow<rhs.mnStartRow;
+            return lhs.mnStartRow<rhs.mnStartRow;
         }
     };
     struct StyleRowRangeCompEqual
     {
         bool operator() (const RowRangeStyle& lhs, const RowRangeStyle& rhs) 
const
         {
-            return lhs.mnEndRow==rhs.mnStartRow;
+            return lhs.mnStartRow==rhs.mnStartRow;
         }
     };
     typedef ::o3tl::sorted_vector< RowRangeStyle, StyleRowRangeComp > 
RowStyles;
commit f61c4b9eb55ecf7d354c58d2ca4753ee33d69f77
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Fri Mar 11 08:49:17 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:29 2022 +0200

    make svx::frame::Style ctor inline
    
    ScDocument: :FillInfo() may result in calling it often.
    Change-Id: I05d0582befc57c4959c33fae6dec3d340b8a49ba
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131338
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/include/svx/framelink.hxx b/include/svx/framelink.hxx
index 2ee2a6f556e0..ae35185a4eb6 100644
--- a/include/svx/framelink.hxx
+++ b/include/svx/framelink.hxx
@@ -167,6 +167,17 @@ public:
 
 inline bool operator>( const Style& rL, const Style& rR ) { return 
rR.operator<(rL); }
 
+inline Style::Style()
+    : mfPrim(0)
+    , mfDist(0)
+    , mfSecn(0)
+    , mfPatternScale(1.0)
+    , meRefMode(RefMode::Centered)
+    , mnType(SvxBorderLineStyle::SOLID)
+    , mbWordTableCell(false)
+    , mbUseGapColor(false)
+{}
+
 }
 
 
diff --git a/svx/source/dialog/framelink.cxx b/svx/source/dialog/framelink.cxx
index 6400eb33d8b0..77098bd3e80d 100644
--- a/svx/source/dialog/framelink.cxx
+++ b/svx/source/dialog/framelink.cxx
@@ -31,11 +31,6 @@ using namespace editeng;
 namespace svx::frame
 {
 
-Style::Style()
-{
-    Clear();
-}
-
 Style::Style( double nP, double nD, double nS, SvxBorderLineStyle nType, 
double fScale )
 {
     Clear();
commit 8113cc4aac983d0b04df765efa630d1f8c9dbace
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Thu Mar 10 15:03:25 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:21 2022 +0200

    load ods/xlsx with full row attributes without allocating all columns
    
    If there's e.g. an entire row bold, it's enough to set default
    attribute for unallocated columns.
    
    This also reverts the workaround from commit 297ab561c6754, as it's
    no longer necessary.
    
    Change-Id: I0b208709aeaff1c0d59da2410926876715cfe642
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131320
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/compilerplugins/clang/redundantfcast.cxx 
b/compilerplugins/clang/redundantfcast.cxx
index 67ff2c56edef..2cfe5183ed46 100644
--- a/compilerplugins/clang/redundantfcast.cxx
+++ b/compilerplugins/clang/redundantfcast.cxx
@@ -329,7 +329,8 @@ public:
         if (fn == SRCDIR "/bridges/source/jni_uno/jni_bridge.cxx")
             return false;
         // TODO constructing a temporary to pass to a && param
-        if (fn == SRCDIR "/sc/source/ui/view/viewfunc.cxx")
+        if (fn == SRCDIR "/sc/source/ui/view/viewfunc.cxx"
+            || fn == SRCDIR "/sc/source/core/data/table2.cxx")
             return false;
         // tdf#145203: FIREBIRD cannot create a table
         if (fn == SRCDIR 
"/connectivity/source/drivers/firebird/DatabaseMetaData.cxx")
diff --git a/sc/inc/attarray.hxx b/sc/inc/attarray.hxx
index 34d1403bcc91..f4028b844c62 100644
--- a/sc/inc/attarray.hxx
+++ b/sc/inc/attarray.hxx
@@ -82,6 +82,10 @@ struct ScAttrEntry
 {
     SCROW                   nEndRow;
     const ScPatternAttr*    pPattern;
+    bool operator==( const ScAttrEntry& other ) const
+    {
+        return nEndRow == other.nEndRow && pPattern == other.pPattern;
+    }
 };
 
 class ScAttrArray
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 3fafb4adedca..1361ba514f2e 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -140,6 +140,8 @@ public:
         return static_cast<const T&>(GetAttr(nRow, sal_uInt16(nWhich), 
nStartRow, nEndRow));
     }
 
+    void        SetAttrEntries(std::vector<ScAttrEntry> && vNewData);
+
     const ScPatternAttr*    GetPattern( SCROW nRow ) const;
     const ScPatternAttr*    GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow 
) const;
     SCROW       ApplySelectionCache( SfxItemPoolCache* pCache, const 
ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged,
@@ -1020,4 +1022,9 @@ inline void ScColumn::SetPatternArea( SCROW nStartRow, 
SCROW nEndRow,
     pAttrArray->SetPatternArea( nStartRow, nEndRow, &rPatAttr, 
true/*bPutToPool*/ );
 }
 
+inline void ScColumnData::SetAttrEntries(std::vector<ScAttrEntry> && vNewData)
+{
+    pAttrArray->SetAttrEntries( std::move( vNewData ));
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/inc/documentimport.hxx b/sc/inc/documentimport.hxx
index 0e49e073fd62..c5b8cf5d5a62 100644
--- a/sc/inc/documentimport.hxx
+++ b/sc/inc/documentimport.hxx
@@ -53,6 +53,11 @@ public:
         ~Attrs();
         Attrs& operator=( Attrs const & ) = delete; // MSVC2015 workaround
         Attrs( Attrs const & ) = delete; // MSVC2015 workaround
+        bool operator==(const Attrs& other) const
+        {
+            return mvData == other.mvData && mbLatinNumFmtOnly == 
other.mbLatinNumFmtOnly;
+        }
+        Attrs& operator=( Attrs&& attrs ) = default;
     };
 
     ScDocumentImport() = delete;
@@ -115,11 +120,11 @@ public:
     void fillDownCells(const ScAddress& rPos, SCROW nFillSize);
 
     /**
-     * Set an array of cell attributes to specified column.  This call
+     * Set an array of cell attributes to specified range of columns.  This 
call
      * transfers the ownership of the ScAttrEntry array from the caller to the
      * column.
      */
-    void setAttrEntries( SCTAB nTab, SCCOL nCol, Attrs&& rAttrs );
+    void setAttrEntries( SCTAB nTab, SCCOL nColStart, SCCOL nColEnd, Attrs&& 
rAttrs );
 
     void setRowsVisible(SCTAB nTab, SCROW nRowStart, SCROW nRowEnd, bool 
bVisible);
 
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index d9479117bfee..55321c6965ea 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -749,6 +749,7 @@ public:
     void        ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, SCCOL 
nEndCol, SCROW nEndRow,
                                   const ScPatternAttr& rAttr, ScEditDataArray* 
pDataArray = nullptr,
                                   bool* const pIsChanged = nullptr );
+    void        SetAttrEntries( SCCOL nStartCol, SCCOL nEndCol, 
std::vector<ScAttrEntry> && vNewData);
 
     void        SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr 
);
     const ScPatternAttr* SetPattern( SCCOL nCol, SCROW nRow, 
std::unique_ptr<ScPatternAttr> );
diff --git a/sc/qa/unit/subsequent_export_test2.cxx 
b/sc/qa/unit/subsequent_export_test2.cxx
index 50ca9ddba05a..ad3f125db7d9 100644
--- a/sc/qa/unit/subsequent_export_test2.cxx
+++ b/sc/qa/unit/subsequent_export_test2.cxx
@@ -3096,7 +3096,7 @@ void ScExportTest2::testWholeRowBold()
     ScDocShellRef xDocSh2 = saveAndReload(*xDocSh1, FORMAT_ODS);
     CPPUNIT_ASSERT(xDocSh2.is());
     pDoc = &xDocSh2->GetDocument();
-    // TODO CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
+    CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
     vcl::Font aFont;
     pDoc->GetPattern(pDoc->MaxCol(), 1, 0)->GetFont(aFont, SC_AUTOCOL_RAW);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, 
aFont.GetWeight());
@@ -3104,7 +3104,7 @@ void ScExportTest2::testWholeRowBold()
     ScDocShellRef xDocSh3 = saveAndReload(*xDocSh2, FORMAT_XLSX);
     CPPUNIT_ASSERT(xDocSh3.is());
     pDoc = &xDocSh3->GetDocument();
-    // TODO CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
+    CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
     pDoc->GetPattern(pDoc->MaxCol(), 1, 0)->GetFont(aFont, SC_AUTOCOL_RAW);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, 
aFont.GetWeight());
 
diff --git a/sc/source/core/data/documentimport.cxx 
b/sc/source/core/data/documentimport.cxx
index d47d50a4cb35..87a14416925b 100644
--- a/sc/source/core/data/documentimport.cxx
+++ b/sc/source/core/data/documentimport.cxx
@@ -593,21 +593,20 @@ void ScDocumentImport::fillDownCells(const ScAddress& 
rPos, SCROW nFillSize)
     }
 }
 
-void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nCol, Attrs&& rAttrs )
+void ScDocumentImport::setAttrEntries( SCTAB nTab, SCCOL nColStart, SCCOL 
nColEnd, Attrs&& rAttrs )
 {
     ScTable* pTab = mpImpl->mrDoc.FetchTable(nTab);
     if (!pTab)
         return;
 
-    ScColumn* pCol = pTab->FetchColumn(nCol);
-    if (!pCol)
-        return;
-
-    ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol);
-    if (pColAttr)
-        pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly;
+    for(SCCOL nCol = nColStart; nCol <= nColEnd; ++nCol )
+    {
+        ColAttr* pColAttr = mpImpl->getColAttr(nTab, nCol);
+        if (pColAttr)
+            pColAttr->mbLatinNumFmtOnly = rAttrs.mbLatinNumFmtOnly;
+    }
 
-    pCol->pAttrArray->SetAttrEntries(std::move(rAttrs.mvData));
+    pTab->SetAttrEntries( nColStart, nColEnd, std::move( rAttrs.mvData ));
 }
 
 void ScDocumentImport::setRowsVisible(SCTAB nTab, SCROW nRowStart, SCROW 
nRowEnd, bool bVisible)
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index cada997f556a..9142ae62aba7 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -2889,6 +2889,37 @@ void ScTable::ApplyPatternArea( SCCOL nStartCol, SCROW 
nStartRow, SCCOL nEndCol,
         CreateColumnIfNotExists(i).ApplyPatternArea(nStartRow, nEndRow, rAttr, 
pDataArray, pIsChanged);
 }
 
+void ScTable::SetAttrEntries( SCCOL nStartCol, SCCOL nEndCol, 
std::vector<ScAttrEntry> && vNewData)
+{
+    if (!ValidCol(nStartCol) || !ValidCol(nEndCol))
+        return;
+    if ( nEndCol == rDocument.MaxCol() )
+    {
+        if ( nStartCol < aCol.size() )
+        {
+            // If we would like set all columns to same attrs, then change 
only attrs for not existing columns
+            nEndCol = aCol.size() - 1;
+            for (SCCOL i = nStartCol; i <= nEndCol; i++)
+                aCol[i].SetAttrEntries( std::vector<ScAttrEntry>(vNewData));
+            aDefaultColData.SetAttrEntries(std::move(vNewData));
+        }
+        else
+        {
+            CreateColumnIfNotExists( nStartCol - 1 );
+            aDefaultColData.SetAttrEntries(std::move(vNewData));
+        }
+    }
+    else
+    {
+        CreateColumnIfNotExists( nEndCol );
+        for (SCCOL i = nStartCol; i < nEndCol; i++) // all but last need a copy
+            aCol[i].SetAttrEntries( std::vector<ScAttrEntry>(vNewData));
+        aCol[nEndCol].SetAttrEntries( std::move(vNewData));
+    }
+}
+
+
+
 void ScTable::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
         const ScPatternAttr& rPattern, SvNumFormatType nNewType )
 {
diff --git a/sc/source/filter/excel/xistyle.cxx 
b/sc/source/filter/excel/xistyle.cxx
index 4161d47a94df..c556b51ec602 100644
--- a/sc/source/filter/excel/xistyle.cxx
+++ b/sc/source/filter/excel/xistyle.cxx
@@ -1989,6 +1989,9 @@ void XclImpXFRangeBuffer::Finalize()
 
     // apply patterns
     XclImpXFBuffer& rXFBuffer = GetXFBuffer();
+    ScDocumentImport::Attrs aPendingAttrParam;
+    SCCOL pendingColStart = -1;
+    SCCOL pendingColEnd = -1;
     SCCOL nScCol = 0;
     for( const auto& rxColumn : maColumns )
     {
@@ -2026,10 +2029,22 @@ void XclImpXFRangeBuffer::Finalize()
             ScDocumentImport::Attrs aAttrParam;
             aAttrParam.mvData.swap(aAttrs);
             aAttrParam.mbLatinNumFmtOnly = false; // when unsure, set it to 
false.
-            rDocImport.setAttrEntries(nScTab, nScCol, std::move(aAttrParam));
+
+            // Compress setting the attributes, set the same set in one call.
+            if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && 
aAttrParam == aPendingAttrParam )
+                ++pendingColEnd;
+            else
+            {
+                if( pendingColStart != -1 )
+                    rDocImport.setAttrEntries(nScTab, pendingColStart, 
pendingColEnd, std::move(aPendingAttrParam));
+                pendingColStart = pendingColEnd = nScCol;
+                aPendingAttrParam = std::move( aAttrParam );
+            }
         }
         ++nScCol;
     }
+    if( pendingColStart != -1 )
+        rDocImport.setAttrEntries(nScTab, pendingColStart, pendingColEnd, 
std::move(aPendingAttrParam));
 
     // insert hyperlink cells
     for( const auto& [rXclRange, rUrl] : maHyperlinks )
diff --git a/sc/source/filter/oox/sheetdatabuffer.cxx 
b/sc/source/filter/oox/sheetdatabuffer.cxx
index b4d6158f66fe..6dbec9e6bd9d 100644
--- a/sc/source/filter/oox/sheetdatabuffer.cxx
+++ b/sc/source/filter/oox/sheetdatabuffer.cxx
@@ -482,6 +482,9 @@ void SheetDataBuffer::finalizeImport()
 
     ScDocument& rDoc = rDocImport.getDoc();
     StylesBuffer& rStyles = getStyles();
+    ScDocumentImport::Attrs aPendingAttrParam;
+    SCCOL pendingColStart = -1;
+    SCCOL pendingColEnd = -1;
     for ( const auto& [rCol, rRowStyles] : maStylesPerColumn )
     {
         SCCOL nScCol = static_cast< SCCOL >( rCol );
@@ -529,8 +532,19 @@ void SheetDataBuffer::finalizeImport()
         aAttrParam.mvData.swap(aAttrs.maAttrs);
         aAttrParam.mbLatinNumFmtOnly = aAttrs.mbLatinNumFmtOnly;
 
-        rDocImport.setAttrEntries(getSheetIndex(), nScCol, 
std::move(aAttrParam));
+        // Compress setting the attributes, set the same set in one call.
+        if( pendingColStart != -1 && pendingColEnd == nScCol - 1 && aAttrParam 
== aPendingAttrParam )
+            ++pendingColEnd;
+        else
+        {
+            if( pendingColStart != -1 )
+                rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, 
pendingColEnd, std::move(aPendingAttrParam));
+            pendingColStart = pendingColEnd = nScCol;
+            aPendingAttrParam = std::move( aAttrParam );
+        }
     }
+    if( pendingColStart != -1 )
+        rDocImport.setAttrEntries(getSheetIndex(), pendingColStart, 
pendingColEnd, std::move(aPendingAttrParam));
 
     // merge all cached merged ranges and update right/bottom cell borders
     for( const auto& rMergedRange : maMergedRanges )
diff --git a/sc/source/filter/xml/xmlcoli.cxx b/sc/source/filter/xml/xmlcoli.cxx
index 00514f830305..502690fe2f03 100644
--- a/sc/source/filter/xml/xmlcoli.cxx
+++ b/sc/source/filter/xml/xmlcoli.cxx
@@ -93,7 +93,6 @@ void SAL_CALL ScXMLTableColContext::endFastElement( sal_Int32 
/*nElement*/ )
             nLastColumn = pDoc->MaxCol();
         if (nCurrentColumn > pDoc->MaxCol())
             nCurrentColumn = pDoc->MaxCol();
-        pDoc->CreateColumnIfNotExists(nSheet, nLastColumn);
         uno::Reference<table::XColumnRowRange> xColumnRowRange 
(xSheet->getCellRangeByPosition(nCurrentColumn, 0, nLastColumn, 0), 
uno::UNO_QUERY);
         if (xColumnRowRange.is())
         {
commit 7252170e4e1ea7348b8c66022dcec8002541ea8e
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Thu Mar 10 18:00:28 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:12 2022 +0200

    don't bother drawing zero-width cells
    
    If a document has most columns hidden, then they'd be "drawn"
    anyway, since ScGridWindow::Draw() calls ExtendHidden(), this appears
    to be needed in order to drawn properly background after the last
    cell. So at least try avoid zero-sized draw operations which
    may add up enough to be noticeable with 16k columns
    
    Change-Id: I631e3be7467f8ea2dbfb824e647f900a7573da61
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131326
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/source/ui/view/output.cxx b/sc/source/ui/view/output.cxx
index 279225079d79..82126f68fe1d 100644
--- a/sc/source/ui/view/output.cxx
+++ b/sc/source/ui/view/output.cxx
@@ -1093,6 +1093,27 @@ void ScOutputData::DrawBackground(vcl::RenderContext& 
rRenderContext)
 
                     nOldMerged = nMergedCols;
 
+                    tools::Long nNewPosX = nPosX;
+                    // extend for all merged cells
+                    nMergedCols = 1;
+                    if (pInfo->bMerged && pInfo->pPatternAttr)
+                    {
+                            const ScMergeAttr* pMerge =
+                                    &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
+                            nMergedCols = std::max<SCCOL>(1, 
pMerge->GetColMerge());
+                    }
+
+                    for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
+                    {
+                        SCCOL nCol = nX+nOldMerged+nMerged;
+                        if (nCol > nX2+2)
+                            break;
+                        nNewPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * 
nLayoutSign;
+                    }
+
+                    if (nNewPosX == nPosX)
+                        continue; // Zero width, no need to draw.
+
                     if (bCellContrast)
                     {
                         //  high contrast for cell borders and backgrounds -> 
empty background
@@ -1136,22 +1157,7 @@ void ScOutputData::DrawBackground(vcl::RenderContext& 
rRenderContext)
 
                     drawCells(rRenderContext, pColor, pBackground, pOldColor, 
pOldBackground, aRect, nPosXLogic, nLayoutSign, nOneXLogic, nOneYLogic, 
pDataBarInfo, pOldDataBarInfo, pIconSetInfo, pOldIconSetInfo, 
mpDoc->GetIconSetBitmapMap());
 
-                    // extend for all merged cells
-                    nMergedCols = 1;
-                    if (pInfo->bMerged && pInfo->pPatternAttr)
-                    {
-                            const ScMergeAttr* pMerge =
-                                    &pInfo->pPatternAttr->GetItem(ATTR_MERGE);
-                            nMergedCols = std::max<SCCOL>(1, 
pMerge->GetColMerge());
-                    }
-
-                    for (SCCOL nMerged = 0; nMerged < nMergedCols; ++nMerged)
-                    {
-                        SCCOL nCol = nX+nOldMerged+nMerged;
-                        if (nCol > nX2+2)
-                            break;
-                        nPosX += pRowInfo[0].basicCellInfo(nCol-1).nWidth * 
nLayoutSign;
-                    }
+                    nPosX = nNewPosX;
                 }
 
                 tools::Long nPosXLogic = nPosX;
commit 76a5b2109a4638c9151af1b8462bea1548d61be6
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Thu Mar 10 16:40:26 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:16:04 2022 +0200

    optimize searching for last visible row
    
    If all the remaining rows until the end of the sheet are hidden
    (which apparently some users do to make the sheet look "nicer"),
    don't search all the rows one by one.
    
    Change-Id: I5f7ef61ece62f7858d4d8cfdf53bb514daa5b504
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131323
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx
index 5862d87a8fa0..523044c7531a 100644
--- a/sc/source/ui/view/tabview.cxx
+++ b/sc/source/ui/view/tabview.cxx
@@ -1330,15 +1330,15 @@ namespace
 
 SCROW lcl_LastVisible( const ScViewData& rViewData )
 {
-    // If many rows are hidden at end of the document (what kind of idiot does 
that?),
+    // If many rows are hidden at end of the document,
     // then there should not be a switch to wide row headers because of this
-    //! as a member to the document???
     ScDocument& rDoc = rViewData.GetDocument();
     SCTAB nTab = rViewData.GetTabNo();
 
     SCROW nVis = rDoc.MaxRow();
-    while ( nVis > 0 && rDoc.GetRowHeight( nVis, nTab ) == 0 )
-        --nVis;
+    SCROW startRow;
+    while ( nVis > 0 && rDoc.GetRowHeight( nVis, nTab, &startRow, nullptr ) == 
0 )
+        nVis = std::max<SCROW>( startRow - 1, 0 );
     return nVis;
 }
 
commit c99a6bf0adb9390f2dd34bf5dee4b1a21e9ae067
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Apr 12 15:19:38 2022 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:15:56 2022 +0200

    don't ignore GetDefPattern() in ScHorizontalAttrIterator
    
    As said in the previous commit, the default pattern is the default
    style that can be edited by the user, so it's principially incorrect
    to simply ignore it. If needed for performance, then it needs
    to be done explicitly. This change currently should not affect
    anything, as ScHorizontalAttrIterator is used only in tests.
    
    Change-Id: I31f153d427cdfd6e98a4d7a3584cfa89676d4c33

diff --git a/sc/inc/dociter.hxx b/sc/inc/dociter.hxx
index 2aee6ac950c1..99ceb1e99972 100644
--- a/sc/inc/dociter.hxx
+++ b/sc/inc/dociter.hxx
@@ -499,8 +499,6 @@ public:
     bool            GetNext( double& rValue, FormulaError& rErr );
 };
 
-//  returns all areas with non-default formatting (horizontal)
-
 class ScHorizontalAttrIterator
 {
 private:
@@ -518,11 +516,9 @@ private:
                               ppPatterns;
     SCCOL                   nCol;
     SCROW                   nRow;
-    bool                    bRowEmpty;
     SCROW                   nMinNextEnd;
 
     void InitForNextRow(bool bInitialization);
-    bool InitForNextAttr();
 
 public:
             ScHorizontalAttrIterator( ScDocument& rDocument, SCTAB nTable,
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index 814bc4b77d1b..8323ca310298 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -147,6 +147,7 @@ public:
     void testValueIterator();
     void testHorizontalAttrIterator();
     void testIteratorsUnallocatedColumnsAttributes();
+    void testIteratorsDefPattern();
 
     /**
      * More direct test for cell broadcaster management, used to track formula
@@ -278,6 +279,7 @@ public:
     CPPUNIT_TEST(testValueIterator);
     CPPUNIT_TEST(testHorizontalAttrIterator);
     CPPUNIT_TEST(testIteratorsUnallocatedColumnsAttributes);
+    CPPUNIT_TEST(testIteratorsDefPattern);
     CPPUNIT_TEST(testCellBroadcaster);
     CPPUNIT_TEST(testFuncParam);
     CPPUNIT_TEST(testNamedRange);
@@ -1388,12 +1390,15 @@ void Test::testHorizontalAttrIterator()
         SCCOL nCol1, nCol2;
         SCROW nRow;
         size_t nCheckPos = 0;
-        for (const ScPatternAttr* pAttr = aIter.GetNext(nCol1, nCol2, nRow); 
pAttr; pAttr = aIter.GetNext(nCol1, nCol2, nRow), ++nCheckPos)
+        for (const ScPatternAttr* pAttr = aIter.GetNext(nCol1, nCol2, nRow); 
pAttr; pAttr = aIter.GetNext(nCol1, nCol2, nRow))
         {
-              CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", 
nCheckPos < nCheckLen);
-              CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][0], 
static_cast<int>(nCol1));
-              CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][1], 
static_cast<int>(nCol2));
-              CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][2], 
static_cast<int>(nRow));
+            if( pAttr == m_pDoc->GetDefPattern())
+                continue;
+            CPPUNIT_ASSERT_MESSAGE("Iteration longer than expected.", 
nCheckPos < nCheckLen);
+            CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][0], 
static_cast<int>(nCol1));
+            CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][1], 
static_cast<int>(nCol2));
+            CPPUNIT_ASSERT_EQUAL(aChecks[nCheckPos][2], 
static_cast<int>(nRow));
+            ++nCheckPos;
         }
     }
 
@@ -1452,6 +1457,56 @@ void Test::testIteratorsUnallocatedColumnsAttributes()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testIteratorsDefPattern()
+{
+    m_pDoc->InsertTab(0, "Tab1");
+
+    // The default pattern is the default style, which can be edited by the 
user.
+    // As such iterators should not ignore it by default, because it might 
contain
+    // some attributes set.
+
+    // Set cells as bold, default allocated, bold, default unallocated.
+    SCCOL firstCol = 100;
+    SCCOL lastCol = 103;
+    ScPatternAttr boldAttr(m_pDoc->GetPool());
+    boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
+    m_pDoc->ApplyPattern(100, 0, 0, boldAttr);
+    m_pDoc->ApplyPattern(102, 0, 0, boldAttr);
+
+    CPPUNIT_ASSERT_EQUAL(SCCOL(102 + 1), m_pDoc->GetAllocatedColumnsCount(0));
+    const ScPatternAttr* pattern = m_pDoc->GetPattern(100, 0, 0);
+    CPPUNIT_ASSERT(pattern != m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT(m_pDoc->GetPattern(102, 0, 0) == pattern);
+    CPPUNIT_ASSERT(m_pDoc->GetPattern(101, 0, 0) == m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT(m_pDoc->GetPattern(103, 0, 0) == m_pDoc->GetDefPattern());
+
+    // Test iterators.
+    ScDocAttrIterator docit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
+    SCCOL col1, col2;
+    SCROW row1, row2;
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == pattern);
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == pattern);
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == nullptr );
+
+    ScAttrRectIterator rectit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == pattern);
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == pattern);
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
+
+    ScHorizontalAttrIterator horit( *m_pDoc, 0, firstCol, 0, lastCol, 0 );
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == pattern);
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == pattern);
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == 
m_pDoc->GetDefPattern());
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == nullptr );
+
+    m_pDoc->DeleteTab(0);
+}
+
 namespace {
 
 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const 
ScAddress& rTo)
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 6bb47df2a9d8..01583d570d63 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -2396,7 +2396,6 @@ ScHorizontalAttrIterator::ScHorizontalAttrIterator( 
ScDocument& rDocument, SCTAB
 
     nRow = nStartRow;
     nCol = nStartCol;
-    bRowEmpty = false;
 
     pIndices.reset( new SCSIZE[nEndCol-nStartCol+1] );
     pNextEnd.reset( new SCROW[nEndCol-nStartCol+1] );
@@ -2412,7 +2411,6 @@ ScHorizontalAttrIterator::~ScHorizontalAttrIterator()
 
 void ScHorizontalAttrIterator::InitForNextRow(bool bInitialization)
 {
-    bool bEmpty = true;
     nMinNextEnd = rDoc.MaxRow();
     SCCOL nThisHead = 0;
 
@@ -2440,18 +2438,12 @@ void ScHorizontalAttrIterator::InitForNextRow(bool 
bInitialization)
             {
                 pNextEnd[nPos] = rDoc.MaxRow();
                 assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
-                ppPatterns[nPos] = nullptr;
+                ppPatterns[nPos] = rDoc.GetDefPattern();
             }
             else if ( nIndex < pArray.Count() )
             {
                 const ScPatternAttr* pPattern = pArray.mvData[nIndex].pPattern;
                 SCROW nThisEnd = pArray.mvData[nIndex].nEndRow;
-
-                if ( IsDefaultItem( pPattern ) )
-                    pPattern = nullptr;
-                else
-                    bEmpty = false; // Found attributes
-
                 pNextEnd[nPos] = nThisEnd;
                 assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
                 ppPatterns[nPos] = pPattern;
@@ -2463,8 +2455,6 @@ void ScHorizontalAttrIterator::InitForNextRow(bool 
bInitialization)
                 ppPatterns[nPos] = nullptr;
             }
         }
-        else if ( ppPatterns[nPos] )
-            bEmpty = false; // Area not at the end yet
 
         if ( nMinNextEnd > pNextEnd[nPos] )
             nMinNextEnd = pNextEnd[nPos];
@@ -2477,24 +2467,7 @@ void ScHorizontalAttrIterator::InitForNextRow(bool 
bInitialization)
         }
     }
 
-    if (bEmpty)
-        nRow = nMinNextEnd; // Skip until end of next section
-    else
-        pHorizEnd[nThisHead] = nEndCol; // set the end position of the last 
horizontal group, too
-    bRowEmpty = bEmpty;
-}
-
-bool ScHorizontalAttrIterator::InitForNextAttr()
-{
-    if ( !ppPatterns[nCol-nStartCol] ) // Skip default items
-    {
-        assert( pHorizEnd[nCol-nStartCol] < rDoc.MaxCol()+1 && "missing stored 
data" );
-        nCol = pHorizEnd[nCol-nStartCol] + 1;
-        if ( nCol > nEndCol )
-            return false;
-    }
-
-    return true;
+    pHorizEnd[nThisHead] = nEndCol; // set the end position of the last 
horizontal group, too
 }
 
 const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& 
rCol2, SCROW& rRow )
@@ -2502,7 +2475,7 @@ const ScPatternAttr* ScHorizontalAttrIterator::GetNext( 
SCCOL& rCol1, SCCOL& rCo
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     for (;;)
     {
-        if ( !bRowEmpty && nCol <= nEndCol && InitForNextAttr() )
+        if ( nCol <= nEndCol )
         {
             const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol];
             rRow = nRow;
@@ -2520,7 +2493,7 @@ const ScPatternAttr* ScHorizontalAttrIterator::GetNext( 
SCCOL& rCol1, SCCOL& rCo
             return nullptr; // Found nothing
         nCol = nStartCol; // Start at the left again
 
-        if ( bRowEmpty || nRow > nMinNextEnd )
+        if ( nRow > nMinNextEnd )
             InitForNextRow(false);
     }
 }
commit cb185c0a7f079ba4ce945c30e6823d377dc97954
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Tue Apr 12 14:58:33 2022 +0200
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:15:49 2022 +0200

    don't artificially clamp attribute iterators range
    
    Even ScDocument::GetDefPattern() can be modified by the user (it's
    the default style that can be edited), so it's conceptually
    incorrect to ignore it while iterating, and clamping to allocated
    columns is also no longer needed. If this makes some code slow,
    then that needs explicit handling in that code or some other
    way of speeding things up.
    
    Change-Id: I4a7c7fef0a8625b559bbce4580df19a5e9ed92a7

diff --git a/sc/inc/attarray.hxx b/sc/inc/attarray.hxx
index c08da494c142..34d1403bcc91 100644
--- a/sc/inc/attarray.hxx
+++ b/sc/inc/attarray.hxx
@@ -221,7 +221,6 @@ public:
     bool    Reserve( SCSIZE nReserve );
     SCSIZE  Count() const { return mvData.size(); }
     SCSIZE  Count( SCROW nRow1, SCROW nRow2 ) const;
-    bool    HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const;
 
 private:
     const ScPatternAttr* SetPatternAreaImpl( SCROW nStartRow, SCROW nEndRow, 
const ScPatternAttr* pPattern,
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index e8a90cbd1f77..3fafb4adedca 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -164,7 +164,6 @@ public:
     std::unique_ptr<ScAttrIterator> CreateAttrIterator( SCROW nStartRow, SCROW 
nEndRow ) const;
 
     bool        IsAllAttrEqual( const ScColumnData& rCol, SCROW nStartRow, 
SCROW nEndRow ) const;
-    bool        HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const;
 
     void        ClearSelectionItems( const sal_uInt16* pWhich, const 
ScMarkData& rMark, SCCOL nCol );
     void        ChangeSelectionIndent( bool bIncrement, const ScMarkData& 
rMark, SCCOL nCol );
@@ -858,11 +857,6 @@ inline bool ScColumnData::IsAllAttrEqual( const 
ScColumnData& rCol, SCROW nStart
     return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow );
 }
 
-inline bool ScColumnData::HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) 
const
-{
-    return pAttrArray->HasNonDefPattern( nStartRow, nEndRow );
-}
-
 inline bool ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW 
nStartRow, SCROW nEndRow ) const
 {
     return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow );
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index f2f0ab196fa7..d9e9b22be9f6 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -819,15 +819,6 @@ public:
 
     SC_DLLPUBLIC SCCOL ClampToAllocatedColumns(SCTAB nTab, SCCOL nCol) const;
     SC_DLLPUBLIC SCCOL GetAllocatedColumnsCount(SCTAB nTab) const;
-    // Limits nCol to the last column that can possibly have a non-default 
pattern. The purpose
-    // is to avoid checking unallocated columns if they don't have attributes 
set, so this is
-    // never less than ClampToAllocatedColumns().
-    SCCOL ClampToMaxNonDefPatternColumn(SCTAB nTab, SCCOL nCol, SCROW nRow1, 
SCROW nRow2) const;
-    SCCOL ClampToMaxNonDefPatternColumn(SCTAB nTab, SCCOL nCol) const
-        { return ClampToMaxNonDefPatternColumn(nTab, nCol, 0, MaxRow()); }
-    SCCOL GetMaxNonDefPatternColumnsCount(SCTAB nTab, SCROW nRow1, SCROW 
nRow2) const;
-    SCCOL GetMaxNonDefPatternColumnsCount(SCTAB nTab) const
-        { return GetMaxNonDefPatternColumnsCount(nTab, 0, MaxRow()); }
 
     SC_DLLPUBLIC ScDBCollection* GetDBCollection() const { return 
pDBCollection.get();}
     void                         SetDBCollection( 
std::unique_ptr<ScDBCollection> pNewDBCollection,
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index e39a921a0926..d9479117bfee 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1113,12 +1113,6 @@ public:
     ScColumnsRange GetColumnsRange(SCCOL begin, SCCOL end) const;
     SCCOL ClampToAllocatedColumns(SCCOL nCol) const { return std::min(nCol, 
static_cast<SCCOL>(aCol.size() - 1)); }
     SCCOL GetAllocatedColumnsCount() const { return aCol.size(); }
-    SCCOL ClampToMaxNonDefPatternColumn(SCCOL nCol, SCROW nRow1, SCROW nRow2) 
const;
-    SCCOL ClampToMaxNonDefPatternColumn(SCCOL nCol) const
-        { return ClampToMaxNonDefPatternColumn(nCol, 0, rDocument.MaxRow()); }
-    SCCOL GetMaxNonDefPatternColumnsCount(SCROW nRow1, SCROW nRow2) const;
-    SCCOL GetMaxNonDefPatternColumnsCount() const
-        { return GetMaxNonDefPatternColumnsCount(0, rDocument.MaxRow()); }
 
     /**
      * Serializes the sheet's geometry data.
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
index e74edd94a434..5af0e3b3609d 100644
--- a/sc/source/core/data/attarray.cxx
+++ b/sc/source/core/data/attarray.cxx
@@ -2689,24 +2689,4 @@ SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW 
nEndRow ) const
     return nIndex2 - nIndex1 + 1;
 }
 
-bool ScAttrArray::HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const
-{
-    if ( mvData.empty() )
-        return false;
-
-    SCSIZE  nIndex1, nIndex2;
-
-    if( !Search( nStartRow, nIndex1 ) )
-        return false;
-
-    if( !Search( nEndRow, nIndex2 ) )
-        nIndex2 = mvData.size() - 1;
-
-    const ScPatternAttr* defPattern = rDocument.GetDefPattern();
-    for( SCSIZE index = nIndex1; index <= nIndex2; ++index )
-        if( mvData[index].pPattern != defPattern )
-            return true;
-    return false;
-}
-
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 35148072f387..6bb47df2a9d8 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -2394,8 +2394,6 @@ ScHorizontalAttrIterator::ScHorizontalAttrIterator( 
ScDocument& rDocument, SCTAB
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     assert(rDoc.maTabs[nTab]);
 
-    nEndCol = rDoc.maTabs[nTab]->ClampToMaxNonDefPatternColumn(nEndCol);
-
     nRow = nStartRow;
     nCol = nStartCol;
     bRowEmpty = false;
@@ -2644,14 +2642,7 @@ ScDocAttrIterator::ScDocAttrIterator(ScDocument& 
rDocument, SCTAB nTable,
     nCol( nCol1 )
 {
     if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
-    {
-        SCCOL attrColCount = 
rDoc.maTabs[nTab]->GetMaxNonDefPatternColumnsCount(nStartRow, nEndRow);
-        if( nCol < attrColCount )
-        {
-            nEndCol = std::min<SCCOL>(nEndCol,attrColCount - 1);
-            pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( 
nStartRow, nEndRow );
-        }
-    }
+        pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( 
nStartRow, nEndRow );
 }
 
 ScDocAttrIterator::~ScDocAttrIterator()
@@ -2780,16 +2771,11 @@ ScAttrRectIterator::ScAttrRectIterator(ScDocument& 
rDocument, SCTAB nTable,
 {
     if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
     {
-        SCCOL attrColCount = 
rDoc.maTabs[nTab]->GetMaxNonDefPatternColumnsCount(nStartRow, nEndRow);
-        if( nCol1 < attrColCount )
-        {
-            nEndCol = std::min<SCCOL>(nEndCol,attrColCount - 1);
-            pColIter = 
rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, 
nEndRow );
-            while ( nIterEndCol < nEndCol &&
-                    rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
-                        rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), 
nStartRow, nEndRow ) )
-                ++nIterEndCol;
-        }
+        pColIter = 
rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, 
nEndRow );
+        while ( nIterEndCol < nEndCol &&
+                rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
+                    rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), nStartRow, 
nEndRow ) )
+            ++nIterEndCol;
     }
 }
 
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index d07d82716404..311a3756d817 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -2697,21 +2697,6 @@ ScColumnsRange ScTable::GetColumnsRange(SCCOL nColBegin, 
SCCOL nColEnd) const
     return ScColumnsRange(ScColumnsRange::Iterator(beginIter), 
ScColumnsRange::Iterator(endIter));
 }
 
-SCCOL ScTable::ClampToMaxNonDefPatternColumn(SCCOL nCol, SCROW nRow1, SCROW 
nRow2) const
-{
-    // The purpose so to avoid unallocated columns if possible, so don't check 
allocated ones.
-    if( nCol < GetAllocatedColumnsCount())
-        return nCol;
-    return std::min<SCCOL>(nCol, GetMaxNonDefPatternColumnsCount( nRow1, nRow2 
) - 1 );
-}
-
-SCCOL ScTable::GetMaxNonDefPatternColumnsCount(SCROW nRow1, SCROW nRow2) const
-{
-    if( aDefaultColData.HasNonDefPattern(nRow1, nRow2))
-        return GetDoc().GetMaxColCount();
-    return GetAllocatedColumnsCount();
-}
-
 // out-of-line the cold part of the CreateColumnIfNotExists function
 void ScTable::CreateColumnIfNotExistsImpl( const SCCOL nScCol ) const
 {
commit 89a7c69589cdd533a4e359ae3c05fb3c733aff97
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Wed Mar 9 15:12:43 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:15:38 2022 +0200

    fix attr iterators to walk even unallocated columns if needed
    
    Things like applying bold to an entire row no longer allocates
    all rows after my recent changes, but the attribute change is
    done in ScTable to the default attribute of unallocated columns.
    That means that clamping column positions to the end of allocated
    columns is no longer valid when handling attributes. Add functions
    that clamp depending on whether unallocated columns have
    a non-default attribute set.
    
    Change-Id: I879d0a034c0b336064361d0f8cb12e5a8da22b9c
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131265
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/inc/attarray.hxx b/sc/inc/attarray.hxx
index 34d1403bcc91..c08da494c142 100644
--- a/sc/inc/attarray.hxx
+++ b/sc/inc/attarray.hxx
@@ -221,6 +221,7 @@ public:
     bool    Reserve( SCSIZE nReserve );
     SCSIZE  Count() const { return mvData.size(); }
     SCSIZE  Count( SCROW nRow1, SCROW nRow2 ) const;
+    bool    HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const;
 
 private:
     const ScPatternAttr* SetPatternAreaImpl( SCROW nStartRow, SCROW nEndRow, 
const ScPatternAttr* pPattern,
diff --git a/sc/inc/column.hxx b/sc/inc/column.hxx
index 23f7df5201bc..e8a90cbd1f77 100644
--- a/sc/inc/column.hxx
+++ b/sc/inc/column.hxx
@@ -161,6 +161,11 @@ public:
     bool        HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) 
const;
     bool        HasAttrib( SCROW nRow, HasAttrFlags nMask, SCROW* nStartRow = 
nullptr, SCROW* nEndRow = nullptr ) const;
 
+    std::unique_ptr<ScAttrIterator> CreateAttrIterator( SCROW nStartRow, SCROW 
nEndRow ) const;
+
+    bool        IsAllAttrEqual( const ScColumnData& rCol, SCROW nStartRow, 
SCROW nEndRow ) const;
+    bool        HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const;
+
     void        ClearSelectionItems( const sal_uInt16* pWhich, const 
ScMarkData& rMark, SCCOL nCol );
     void        ChangeSelectionIndent( bool bIncrement, const ScMarkData& 
rMark, SCCOL nCol );
 };
@@ -206,7 +211,6 @@ friend class ScCountIfCellIterator;
 friend class ScFormulaGroupIterator;
 friend class ScCellIterator;
 friend class ScHorizontalCellIterator;
-friend class ScHorizontalAttrIterator;
 friend class ScColumnTextWidthIterator;
 friend class ScDocumentImport;
 friend class sc::DocumentStreamAccess;
@@ -303,7 +307,6 @@ public:
     bool    GetLastVisibleAttr( SCROW& rLastRow ) const;
     bool    HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const;
     bool    IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW 
nEndRow ) const;
-    bool    IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW 
nEndRow ) const;
 
     bool    TestInsertCol( SCROW nStartRow, SCROW nEndRow) const;
     bool TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const;
@@ -352,8 +355,6 @@ public:
         sc::MixDocContext& rCxt, SCROW nRow1, SCROW nRow2, ScPasteFunc 
nFunction, bool bSkipEmpty,
         const ScColumn& rSrcCol );
 
-    std::unique_ptr<ScAttrIterator> CreateAttrIterator( SCROW nStartRow, SCROW 
nEndRow ) const;
-
     void UpdateSelectionFunction(
         const ScRangeList& rRanges, ScFunctionData& rData, const 
ScFlatBoolRowSegments& rHiddenRows );
 
@@ -852,11 +853,16 @@ inline bool ScColumn::IsEmptyAttr() const
     return pAttrArray->IsEmpty();
 }
 
-inline bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, 
SCROW nEndRow ) const
+inline bool ScColumnData::IsAllAttrEqual( const ScColumnData& rCol, SCROW 
nStartRow, SCROW nEndRow ) const
 {
     return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow );
 }
 
+inline bool ScColumnData::HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) 
const
+{
+    return pAttrArray->HasNonDefPattern( nStartRow, nEndRow );
+}
+
 inline bool ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW 
nStartRow, SCROW nEndRow ) const
 {
     return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow );
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
index d9e9b22be9f6..f2f0ab196fa7 100644
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -819,6 +819,15 @@ public:
 
     SC_DLLPUBLIC SCCOL ClampToAllocatedColumns(SCTAB nTab, SCCOL nCol) const;
     SC_DLLPUBLIC SCCOL GetAllocatedColumnsCount(SCTAB nTab) const;
+    // Limits nCol to the last column that can possibly have a non-default 
pattern. The purpose
+    // is to avoid checking unallocated columns if they don't have attributes 
set, so this is
+    // never less than ClampToAllocatedColumns().
+    SCCOL ClampToMaxNonDefPatternColumn(SCTAB nTab, SCCOL nCol, SCROW nRow1, 
SCROW nRow2) const;
+    SCCOL ClampToMaxNonDefPatternColumn(SCTAB nTab, SCCOL nCol) const
+        { return ClampToMaxNonDefPatternColumn(nTab, nCol, 0, MaxRow()); }
+    SCCOL GetMaxNonDefPatternColumnsCount(SCTAB nTab, SCROW nRow1, SCROW 
nRow2) const;
+    SCCOL GetMaxNonDefPatternColumnsCount(SCTAB nTab) const
+        { return GetMaxNonDefPatternColumnsCount(nTab, 0, MaxRow()); }
 
     SC_DLLPUBLIC ScDBCollection* GetDBCollection() const { return 
pDBCollection.get();}
     void                         SetDBCollection( 
std::unique_ptr<ScDBCollection> pNewDBCollection,
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 80117ccf5f42..e39a921a0926 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -262,9 +262,6 @@ friend class ScCellIterator;
 friend class ScQueryCellIterator;
 friend class ScCountIfCellIterator;
 friend class ScHorizontalCellIterator;
-friend class ScHorizontalAttrIterator;
-friend class ScDocAttrIterator;
-friend class ScAttrRectIterator;
 friend class ScColumnTextWidthIterator;
 friend class ScDocumentImport;
 friend class sc::DocumentStreamAccess;
@@ -1116,6 +1113,12 @@ public:
     ScColumnsRange GetColumnsRange(SCCOL begin, SCCOL end) const;
     SCCOL ClampToAllocatedColumns(SCCOL nCol) const { return std::min(nCol, 
static_cast<SCCOL>(aCol.size() - 1)); }
     SCCOL GetAllocatedColumnsCount() const { return aCol.size(); }
+    SCCOL ClampToMaxNonDefPatternColumn(SCCOL nCol, SCROW nRow1, SCROW nRow2) 
const;
+    SCCOL ClampToMaxNonDefPatternColumn(SCCOL nCol) const
+        { return ClampToMaxNonDefPatternColumn(nCol, 0, rDocument.MaxRow()); }
+    SCCOL GetMaxNonDefPatternColumnsCount(SCROW nRow1, SCROW nRow2) const;
+    SCCOL GetMaxNonDefPatternColumnsCount() const
+        { return GetMaxNonDefPatternColumnsCount(0, rDocument.MaxRow()); }
 
     /**
      * Serializes the sheet's geometry data.
diff --git a/sc/qa/unit/subsequent_export_test2.cxx 
b/sc/qa/unit/subsequent_export_test2.cxx
index 769a7f77a60c..50ca9ddba05a 100644
--- a/sc/qa/unit/subsequent_export_test2.cxx
+++ b/sc/qa/unit/subsequent_export_test2.cxx
@@ -216,6 +216,7 @@ public:
     void testTdf142578();
     void testTdf145059();
     void testTdf130104_XLSXIndent();
+    void testWholeRowBold();
 
     CPPUNIT_TEST_SUITE(ScExportTest2);
 
@@ -331,6 +332,7 @@ public:
     CPPUNIT_TEST(testTdf142578);
     CPPUNIT_TEST(testTdf145059);
     CPPUNIT_TEST(testTdf130104_XLSXIndent);
+    CPPUNIT_TEST(testWholeRowBold);
 
     CPPUNIT_TEST_SUITE_END();
 
@@ -3080,6 +3082,37 @@ void ScExportTest2::testTdf130104_XLSXIndent()
     xDocSh->DoClose();
 }
 
+void ScExportTest2::testWholeRowBold()
+{
+    ScDocShellRef xDocSh1 = loadDoc(u"blank.", FORMAT_ODS);
+    CPPUNIT_ASSERT_MESSAGE("Failed to open empty doc", xDocSh1.is());
+    ScDocument* pDoc = &xDocSh1->GetDocument();
+
+    // Make entire second row row bold.
+    ScPatternAttr boldAttr(pDoc->GetPool());
+    boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
+    pDoc->ApplyPatternAreaTab(0, 1, pDoc->MaxCol(), 1, 0, boldAttr);
+
+    ScDocShellRef xDocSh2 = saveAndReload(*xDocSh1, FORMAT_ODS);
+    CPPUNIT_ASSERT(xDocSh2.is());
+    pDoc = &xDocSh2->GetDocument();
+    // TODO CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
+    vcl::Font aFont;
+    pDoc->GetPattern(pDoc->MaxCol(), 1, 0)->GetFont(aFont, SC_AUTOCOL_RAW);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, 
aFont.GetWeight());
+
+    ScDocShellRef xDocSh3 = saveAndReload(*xDocSh2, FORMAT_XLSX);
+    CPPUNIT_ASSERT(xDocSh3.is());
+    pDoc = &xDocSh3->GetDocument();
+    // TODO CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
pDoc->GetAllocatedColumnsCount(0));
+    pDoc->GetPattern(pDoc->MaxCol(), 1, 0)->GetFont(aFont, SC_AUTOCOL_RAW);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, 
aFont.GetWeight());
+
+    xDocSh1->DoClose();
+    xDocSh2->DoClose();
+    xDocSh3->DoClose();
+}
+
 CPPUNIT_TEST_SUITE_REGISTRATION(ScExportTest2);
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
index b51735bad5bd..814bc4b77d1b 100644
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -146,6 +146,7 @@ public:
     void testHorizontalIterator();
     void testValueIterator();
     void testHorizontalAttrIterator();
+    void testIteratorsUnallocatedColumnsAttributes();
 
     /**
      * More direct test for cell broadcaster management, used to track formula
@@ -276,6 +277,7 @@ public:
     CPPUNIT_TEST(testHorizontalIterator);
     CPPUNIT_TEST(testValueIterator);
     CPPUNIT_TEST(testHorizontalAttrIterator);
+    CPPUNIT_TEST(testIteratorsUnallocatedColumnsAttributes);
     CPPUNIT_TEST(testCellBroadcaster);
     CPPUNIT_TEST(testFuncParam);
     CPPUNIT_TEST(testNamedRange);
@@ -1398,6 +1400,58 @@ void Test::testHorizontalAttrIterator()
     m_pDoc->DeleteTab(0);
 }
 
+void Test::testIteratorsUnallocatedColumnsAttributes()
+{
+    m_pDoc->InsertTab(0, "Tab1");
+
+    // Make entire second row and third row bold.
+    ScPatternAttr boldAttr(m_pDoc->GetPool());
+    boldAttr.GetItemSet().Put(SvxWeightItem(WEIGHT_BOLD, ATTR_FONT_WEIGHT));
+    m_pDoc->ApplyPatternAreaTab(0, 1, m_pDoc->MaxCol(), 2, 0, boldAttr);
+
+    // That shouldn't need allocating more columns, just changing the default 
attribute.
+    CPPUNIT_ASSERT_EQUAL(SCCOL(INITIALCOLCOUNT), 
m_pDoc->GetAllocatedColumnsCount(0));
+    vcl::Font aFont;
+    const ScPatternAttr* pattern = m_pDoc->GetPattern(m_pDoc->MaxCol(), 1, 0);
+    pattern->GetFont(aFont, SC_AUTOCOL_RAW);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("font should be bold", WEIGHT_BOLD, 
aFont.GetWeight());
+
+    // Test iterators.
+    ScDocAttrIterator docit( *m_pDoc, 0, INITIALCOLCOUNT - 1, 1, 
INITIALCOLCOUNT, 2 );
+    SCCOL col1, col2;
+    SCROW row1, row2;
+    CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
+    CPPUNIT_ASSERT_EQUAL( SCCOL(INITIALCOLCOUNT - 1), col1 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
+    CPPUNIT_ASSERT_EQUAL( pattern, docit.GetNext( col1, row1, row2 ));
+    CPPUNIT_ASSERT_EQUAL( INITIALCOLCOUNT, col1 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
+    CPPUNIT_ASSERT( docit.GetNext( col1, row1, row2 ) == nullptr );
+
+    ScAttrRectIterator rectit( *m_pDoc, 0, INITIALCOLCOUNT - 1, 1, 
INITIALCOLCOUNT, 2 );
+    CPPUNIT_ASSERT_EQUAL( pattern, rectit.GetNext( col1, col2, row1, row2 ));
+    CPPUNIT_ASSERT_EQUAL( SCCOL(INITIALCOLCOUNT - 1), col1 );
+    CPPUNIT_ASSERT_EQUAL( INITIALCOLCOUNT, col2 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(2), row2 );
+    CPPUNIT_ASSERT( rectit.GetNext( col1, col2, row1, row2 ) == nullptr );
+
+    ScHorizontalAttrIterator horit( *m_pDoc, 0, INITIALCOLCOUNT - 1, 1, 
INITIALCOLCOUNT, 2 );
+    CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
+    CPPUNIT_ASSERT_EQUAL( SCCOL(INITIALCOLCOUNT - 1), col1 );
+    CPPUNIT_ASSERT_EQUAL( INITIALCOLCOUNT, col2 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(1), row1 );
+    CPPUNIT_ASSERT_EQUAL( pattern, horit.GetNext( col1, col2, row1 ));
+    CPPUNIT_ASSERT_EQUAL( SCCOL(INITIALCOLCOUNT - 1), col1 );
+    CPPUNIT_ASSERT_EQUAL( INITIALCOLCOUNT, col2 );
+    CPPUNIT_ASSERT_EQUAL( SCROW(2), row1 );
+    CPPUNIT_ASSERT( horit.GetNext( col1, col2, row1 ) == nullptr );
+
+    m_pDoc->DeleteTab(0);
+}
+
 namespace {
 
 bool broadcasterShifted(const ScDocument& rDoc, const ScAddress& rFrom, const 
ScAddress& rTo)
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
index 5af0e3b3609d..e74edd94a434 100644
--- a/sc/source/core/data/attarray.cxx
+++ b/sc/source/core/data/attarray.cxx
@@ -2689,4 +2689,24 @@ SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW 
nEndRow ) const
     return nIndex2 - nIndex1 + 1;
 }
 
+bool ScAttrArray::HasNonDefPattern( SCROW nStartRow, SCROW nEndRow ) const
+{
+    if ( mvData.empty() )
+        return false;
+
+    SCSIZE  nIndex1, nIndex2;
+
+    if( !Search( nStartRow, nIndex1 ) )
+        return false;
+
+    if( !Search( nEndRow, nIndex2 ) )
+        nIndex2 = mvData.size() - 1;
+
+    const ScPatternAttr* defPattern = rDocument.GetDefPattern();
+    for( SCSIZE index = nIndex1; index <= nIndex2; ++index )
+        if( mvData[index].pPattern != defPattern )
+            return true;
+    return false;
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 966a182cbf7c..84b68b76db39 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -1940,7 +1940,7 @@ void ScColumn::MixData(
     CellStorageModified();
 }
 
-std::unique_ptr<ScAttrIterator> ScColumn::CreateAttrIterator( SCROW nStartRow, 
SCROW nEndRow ) const
+std::unique_ptr<ScAttrIterator> ScColumnData::CreateAttrIterator( SCROW 
nStartRow, SCROW nEndRow ) const
 {
     return std::make_unique<ScAttrIterator>( pAttrArray.get(), nStartRow, 
nEndRow, GetDoc().GetDefPattern() );
 }
diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx
index 28543f720c16..35148072f387 100644
--- a/sc/source/core/data/dociter.cxx
+++ b/sc/source/core/data/dociter.cxx
@@ -2394,7 +2394,7 @@ ScHorizontalAttrIterator::ScHorizontalAttrIterator( 
ScDocument& rDocument, SCTAB
     assert(nTab < rDoc.GetTableCount() && "index out of bounds, FIX IT");
     assert(rDoc.maTabs[nTab]);
 
-    nEndCol = rDoc.maTabs[nTab]->ClampToAllocatedColumns(nEndCol);
+    nEndCol = rDoc.maTabs[nTab]->ClampToMaxNonDefPatternColumn(nEndCol);
 
     nRow = nStartRow;
     nCol = nStartCol;
@@ -2423,14 +2423,13 @@ void ScHorizontalAttrIterator::InitForNextRow(bool 
bInitialization)
         SCCOL nPos = i - nStartCol;
         if ( bInitialization || pNextEnd[nPos] < nRow )
         {
-            const ScAttrArray* pArray = 
rDoc.maTabs[nTab]->aCol[i].pAttrArray.get();
-            assert(pArray);
+            const ScAttrArray& pArray = 
rDoc.maTabs[nTab]->ColumnData(i).AttrArray();
 
             SCSIZE nIndex;
             if (bInitialization)
             {
-                if ( pArray->Count() )
-                    pArray->Search( nStartRow, nIndex );
+                if ( pArray.Count() )
+                    pArray.Search( nStartRow, nIndex );
                 else
                     nIndex = 0;
                 pIndices[nPos] = nIndex;
@@ -2439,16 +2438,16 @@ void ScHorizontalAttrIterator::InitForNextRow(bool 
bInitialization)
             else
                 nIndex = ++pIndices[nPos];
 
-            if ( !nIndex && !pArray->Count() )
+            if ( !nIndex && !pArray.Count() )
             {
                 pNextEnd[nPos] = rDoc.MaxRow();
                 assert( pNextEnd[nPos] >= nRow && "Sequence out of order" );
                 ppPatterns[nPos] = nullptr;
             }
-            else if ( nIndex < pArray->Count() )
+            else if ( nIndex < pArray.Count() )
             {
-                const ScPatternAttr* pPattern = 
pArray->mvData[nIndex].pPattern;
-                SCROW nThisEnd = pArray->mvData[nIndex].nEndRow;
+                const ScPatternAttr* pPattern = pArray.mvData[nIndex].pPattern;
+                SCROW nThisEnd = pArray.mvData[nIndex].nEndRow;
 
                 if ( IsDefaultItem( pPattern ) )
                     pPattern = nullptr;
@@ -2644,11 +2643,14 @@ ScDocAttrIterator::ScDocAttrIterator(ScDocument& 
rDocument, SCTAB nTable,
     nEndRow( nRow2 ),
     nCol( nCol1 )
 {
-    if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab]
-        && nCol < rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
+    if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
     {
-        nEndCol = rDoc.maTabs[nTab]->ClampToAllocatedColumns(nEndCol);
-        pColIter = rDoc.maTabs[nTab]->aCol[nCol].CreateAttrIterator( 
nStartRow, nEndRow );
+        SCCOL attrColCount = 
rDoc.maTabs[nTab]->GetMaxNonDefPatternColumnsCount(nStartRow, nEndRow);
+        if( nCol < attrColCount )
+        {
+            nEndCol = std::min<SCCOL>(nEndCol,attrColCount - 1);
+            pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( 
nStartRow, nEndRow );
+        }
     }
 }
 
@@ -2669,7 +2671,7 @@ const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& 
rCol, SCROW& rRow1, SCRO
 
         ++nCol;
         if ( nCol <= nEndCol )
-            pColIter = rDoc.maTabs[nTab]->aCol[nCol].CreateAttrIterator( 
nStartRow, nEndRow );
+            pColIter = rDoc.maTabs[nTab]->ColumnData(nCol).CreateAttrIterator( 
nStartRow, nEndRow );
         else
             pColIter.reset();
     }
@@ -2776,18 +2778,19 @@ ScAttrRectIterator::ScAttrRectIterator(ScDocument& 
rDocument, SCTAB nTable,
     nIterStartCol( nCol1 ),
     nIterEndCol( nCol1 )
 {
-    if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab]
-        && nCol1 < rDoc.maTabs[nTab]->GetAllocatedColumnsCount())
+    if ( ValidTab(nTab) && nTab < rDoc.GetTableCount() && rDoc.maTabs[nTab] )
     {
-        nEndCol = rDoc.maTabs[nTab]->ClampToAllocatedColumns(nEndCol);
-        pColIter = rDoc.maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( 
nStartRow, nEndRow );
-        while ( nIterEndCol < nEndCol &&
-                rDoc.maTabs[nTab]->aCol[nIterEndCol].IsAllAttrEqual(
-                    rDoc.maTabs[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow 
) )
-            ++nIterEndCol;
+        SCCOL attrColCount = 
rDoc.maTabs[nTab]->GetMaxNonDefPatternColumnsCount(nStartRow, nEndRow);
+        if( nCol1 < attrColCount )
+        {
+            nEndCol = std::min<SCCOL>(nEndCol,attrColCount - 1);
+            pColIter = 
rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, 
nEndRow );
+            while ( nIterEndCol < nEndCol &&
+                    rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
+                        rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), 
nStartRow, nEndRow ) )
+                ++nIterEndCol;
+        }
     }
-    else
-        pColIter = nullptr;
 }
 
 ScAttrRectIterator::~ScAttrRectIterator()
@@ -2799,7 +2802,7 @@ void ScAttrRectIterator::DataChanged()
     if (pColIter)
     {
         SCROW nNextRow = pColIter->GetNextRow();
-        pColIter = rDoc.maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( 
nNextRow, nEndRow );
+        pColIter = 
rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nNextRow, 
nEndRow );
     }
 }
 
@@ -2820,10 +2823,10 @@ const ScPatternAttr* ScAttrRectIterator::GetNext( 
SCCOL& rCol1, SCCOL& rCol2,
         if ( nIterStartCol <= nEndCol )
         {
             nIterEndCol = nIterStartCol;
-            pColIter = 
rDoc.maTabs[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow );
+            pColIter = 
rDoc.maTabs[nTab]->ColumnData(nIterStartCol).CreateAttrIterator( nStartRow, 
nEndRow );
             while ( nIterEndCol < nEndCol &&
-                    rDoc.maTabs[nTab]->aCol[nIterEndCol].IsAllAttrEqual(
-                        rDoc.maTabs[nTab]->aCol[nIterEndCol+1], nStartRow, 
nEndRow ) )
+                    rDoc.maTabs[nTab]->ColumnData(nIterEndCol).IsAllAttrEqual(
+                        rDoc.maTabs[nTab]->ColumnData(nIterEndCol+1), 
nStartRow, nEndRow ) )
                 ++nIterEndCol;
         }
         else
diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx
index 311a3756d817..d07d82716404 100644
--- a/sc/source/core/data/table1.cxx
+++ b/sc/source/core/data/table1.cxx
@@ -2697,6 +2697,21 @@ ScColumnsRange ScTable::GetColumnsRange(SCCOL nColBegin, 
SCCOL nColEnd) const
     return ScColumnsRange(ScColumnsRange::Iterator(beginIter), 
ScColumnsRange::Iterator(endIter));
 }
 
+SCCOL ScTable::ClampToMaxNonDefPatternColumn(SCCOL nCol, SCROW nRow1, SCROW 
nRow2) const
+{
+    // The purpose so to avoid unallocated columns if possible, so don't check 
allocated ones.
+    if( nCol < GetAllocatedColumnsCount())
+        return nCol;
+    return std::min<SCCOL>(nCol, GetMaxNonDefPatternColumnsCount( nRow1, nRow2 
) - 1 );
+}
+
+SCCOL ScTable::GetMaxNonDefPatternColumnsCount(SCROW nRow1, SCROW nRow2) const
+{
+    if( aDefaultColData.HasNonDefPattern(nRow1, nRow2))
+        return GetDoc().GetMaxColCount();
+    return GetAllocatedColumnsCount();
+}
+
 // out-of-line the cold part of the CreateColumnIfNotExists function
 void ScTable::CreateColumnIfNotExistsImpl( const SCCOL nScCol ) const
 {
commit a2f1d8ce529551c0212a492d36fda27f555ef547
Author:     Luboš Luňák <l.lu...@collabora.com>
AuthorDate: Wed Mar 9 11:45:34 2022 +0100
Commit:     Luboš Luňák <l.lu...@collabora.com>
CommitDate: Thu Apr 14 10:15:30 2022 +0200

    add ColumnData() for simple handling of unallocated column data
    
    Move the decision whether to return a column or the default data
    for unallocated columns into a simple function.
    
    Change-Id: I369b8c815de96b61181f2483c6afac44a5c3bc2e
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131264
    Tested-by: Jenkins
    Reviewed-by: Luboš Luňák <l.lu...@collabora.com>

diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
index 9362fee910dd..80117ccf5f42 100644
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -1128,6 +1128,8 @@ public:
 
     std::set<SCCOL> QueryColumnsWithFormulaCells() const;
 
+    const ScColumnData& ColumnData( SCCOL nCol ) const { return nCol < 
aCol.size() ? aCol[ nCol ] : aDefaultColData; }
+
     void CheckIntegrity() const;
 
 private:
diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx
index c1e89152d3ae..cada997f556a 100644
--- a/sc/source/core/data/table2.cxx
+++ b/sc/source/core/data/table2.cxx
@@ -2230,28 +2230,20 @@ const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW 
nRow, sal_uInt16 nWhich )
 {
     if (!ValidColRow(nCol, nRow))
         return nullptr;
-    if (nCol < GetAllocatedColumnsCount())
-        return &aCol[nCol].GetAttr( nRow, nWhich );
-    return &aDefaultColData.GetAttr( nRow, nWhich );
+    return &ColumnData(nCol).GetAttr( nRow, nWhich );
 }
 
 const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, sal_uInt16 
nWhich, SCROW& nStartRow, SCROW& nEndRow ) const
 {
     if (!ValidColRow(nCol, nRow))
         return nullptr;
-    if (nCol < GetAllocatedColumnsCount())
-        return &aCol[nCol].GetAttr( nRow, nWhich, nStartRow, nEndRow );
-    return &aDefaultColData.GetAttr( nRow, nWhich, nStartRow, nEndRow );
+    return &ColumnData(nCol).GetAttr( nRow, nWhich, nStartRow, nEndRow );
 }
 
 sal_uInt32 ScTable::GetNumberFormat( const ScInterpreterContext& rContext, 
const ScAddress& rPos ) const
 {
     if (ValidColRow(rPos.Col(), rPos.Row()))
-    {
-        if (rPos.Col() < GetAllocatedColumnsCount())
-            return aCol[rPos.Col()].GetNumberFormat(rContext, rPos.Row());
-        return aDefaultColData.GetNumberFormat(rContext, rPos.Row());
-    }
+        return ColumnData(rPos.Col()).GetNumberFormat(rContext, rPos.Row());
     return 0;
 }
 
@@ -2265,9 +2257,7 @@ sal_uInt32 ScTable::GetNumberFormat( SCCOL nCol, SCROW 
nStartRow, SCROW nEndRow
     if (!ValidCol(nCol) || !ValidRow(nStartRow) || !ValidRow(nEndRow))
         return 0;
 
-    if (nCol < GetAllocatedColumnsCount())
-        return aCol[nCol].GetNumberFormat(nStartRow, nEndRow);
-    return aDefaultColData.GetNumberFormat(nStartRow, nEndRow);
+    return ColumnData(nCol).GetNumberFormat(nStartRow, nEndRow);
 }
 
 void ScTable::SetNumberFormat( SCCOL nCol, SCROW nRow, sal_uInt32 
nNumberFormat )
@@ -2280,19 +2270,16 @@ void ScTable::SetNumberFormat( SCCOL nCol, SCROW nRow, 
sal_uInt32 nNumberFormat
 
 const ScPatternAttr* ScTable::GetPattern( SCCOL nCol, SCROW nRow ) const
 {
-    if (ValidColRow(nCol,nRow) && nCol < GetAllocatedColumnsCount())
-        return aCol[nCol].GetPattern( nRow );
-    else
-        return aDefaultColData.GetPattern( nRow );
+    if (!ValidColRow(nCol,nRow))
+        return nullptr;
+    return ColumnData(nCol).GetPattern( nRow );
 }
 
 const ScPatternAttr* ScTable::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, 
SCROW nEndRow ) const
 {
-    if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow 
<= nEndRow)
-        && nCol < GetAllocatedColumnsCount())
-        return aCol[nCol].GetMostUsedPattern( nStartRow, nEndRow );
-    else
-        return aDefaultColData.GetMostUsedPattern( nStartRow, nEndRow );
+    if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow 
<= nEndRow))
+        return ColumnData(nCol).GetMostUsedPattern( nStartRow, nEndRow );
+    return nullptr;
 }
 
 bool ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 
HasAttrFlags nMask ) const
@@ -2307,9 +2294,7 @@ bool ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL 
nCol2, SCROW nRow2, Has
 
 bool ScTable::HasAttrib( SCCOL nCol, SCROW nRow, HasAttrFlags nMask, SCROW* 
nStartRow, SCROW* nEndRow ) const
 {
-    if( nCol < aCol.size())
-        return aCol[nCol].HasAttrib( nRow, nMask, nStartRow, nEndRow );
-    return aDefaultColData.HasAttrib( nRow, nMask, nStartRow, nEndRow );
+    return ColumnData(nCol).HasAttrib( nRow, nMask, nStartRow, nEndRow );
 }
 
 bool ScTable::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask 
) const
@@ -3023,10 +3008,7 @@ const ScStyleSheet* ScTable::GetStyle( SCCOL nCol, SCROW 
nRow ) const
 {
     if ( !ValidColRow( nCol, nRow ) )
         return nullptr;
-    if ( nCol < aCol.size() )
-        return aCol[nCol].GetStyle( nRow );
-    else
-        return aDefaultColData.GetStyle( nRow );
+    return ColumnData(nCol).GetStyle( nRow );
 }
 
 const ScStyleSheet* ScTable::GetSelectionStyle( const ScMarkData& rMark, bool& 
rFound ) const

Reply via email to