summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/attarray.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/attarray.cxx')
-rw-r--r--sc/source/core/data/attarray.cxx2690
1 files changed, 2690 insertions, 0 deletions
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx
new file mode 100644
index 000000000..d5eaa1906
--- /dev/null
+++ b/sc/source/core/data/attarray.cxx
@@ -0,0 +1,2690 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <attarray.hxx>
+#include <scitems.hxx>
+#include <editeng/borderline.hxx>
+#include <editeng/boxitem.hxx>
+#include <editeng/lineitem.hxx>
+#include <editeng/shaditem.hxx>
+#include <editeng/editobj.hxx>
+#include <editeng/justifyitem.hxx>
+#include <osl/diagnose.h>
+#include <svl/poolcach.hxx>
+#include <sfx2/objsh.hxx>
+
+#include <global.hxx>
+#include <document.hxx>
+#include <docpool.hxx>
+#include <patattr.hxx>
+#include <stlsheet.hxx>
+#include <stlpool.hxx>
+#include <markarr.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <segmenttree.hxx>
+#include <editdataarray.hxx>
+#include <cellvalue.hxx>
+#include <editutil.hxx>
+#include <mtvelements.hxx>
+#include <memory>
+
+using ::editeng::SvxBorderLine;
+
+ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, ScAttrArray* pDefaultColAttrArray ) :
+ nCol( nNewCol ),
+ nTab( nNewTab ),
+ rDocument( rDoc )
+{
+ if ( nCol == -1 || !pDefaultColAttrArray || pDefaultColAttrArray->mvData.empty() )
+ return;
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd( nCol, 0, nTab );
+ mvData.resize( pDefaultColAttrArray->mvData.size() );
+ for ( size_t nIdx = 0; nIdx < pDefaultColAttrArray->mvData.size(); ++nIdx )
+ {
+ mvData[nIdx].nEndRow = pDefaultColAttrArray->mvData[nIdx].nEndRow;
+ ScPatternAttr aNewPattern( *(pDefaultColAttrArray->mvData[nIdx].pPattern) );
+ mvData[nIdx].pPattern = &rDocument.GetPool()->Put( aNewPattern );
+ bool bNumFormatChanged = false;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ mvData[nIdx].pPattern->GetItemSet(), rDocument.GetDefPattern()->GetItemSet() ) )
+ {
+ aAdrStart.SetRow( nIdx ? mvData[nIdx-1].nEndRow+1 : 0 );
+ aAdrEnd.SetRow( mvData[nIdx].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+}
+
+ScAttrArray::~ScAttrArray()
+{
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (auto const & rEntry : mvData)
+ pDocPool->Remove(*rEntry.pPattern);
+}
+
+#if DEBUG_SC_TESTATTRARRAY
+void ScAttrArray::TestData() const
+{
+
+ sal_uInt16 nErr = 0;
+ SCSIZE nPos;
+ for (nPos=0; nPos<nCount; nPos++)
+ {
+ if (nPos > 0)
+ if (mvData[nPos].pPattern == mvData[nPos-1].pPattern || mvData[nPos].nRow <= mvData[nPos-1].nRow)
+ ++nErr;
+ if (mvData[nPos].pPattern->Which() != ATTR_PATTERN)
+ ++nErr;
+ }
+ if ( nPos && mvData[nPos-1].nRow != rDocument.MaxRow() )
+ ++nErr;
+
+ SAL_WARN_IF( nErr, "sc", nErr << " errors in attribute array, column " << nCol );
+}
+#endif
+
+void ScAttrArray::SetDefaultIfNotInit( SCSIZE nNeeded )
+{
+ if ( !mvData.empty() )
+ return;
+
+ SCSIZE nNewLimit = std::max<SCSIZE>( SC_ATTRARRAY_DELTA, nNeeded );
+ mvData.reserve( nNewLimit );
+ mvData.emplace_back();
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = rDocument.GetDefPattern(); // no put
+}
+
+void ScAttrArray::Reset( const ScPatternAttr* pPattern )
+{
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ for (SCSIZE i=0; i<mvData.size(); i++)
+ {
+ // ensure that attributing changes text width of cell
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ if ( nCol != -1 )
+ {
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ pPattern->GetItemSet(), pOldPattern->GetItemSet() ) )
+ {
+ aAdrStart.SetRow( i ? mvData[i-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[i].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+ pDocPool->Remove(*pOldPattern);
+ }
+ mvData.resize(0);
+
+ rDocument.SetStreamValid(nTab, false);
+
+ mvData.resize(1);
+ const ScPatternAttr* pNewPattern = &pDocPool->Put(*pPattern);
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = pNewPattern;
+}
+
+bool ScAttrArray::Concat(SCSIZE nPos)
+{
+ bool bRet = false;
+ if (nPos < mvData.size())
+ {
+ if (nPos > 0)
+ {
+ if (mvData[nPos - 1].pPattern == mvData[nPos].pPattern)
+ {
+ mvData[nPos - 1].nEndRow = mvData[nPos].nEndRow;
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ mvData.erase(mvData.begin() + nPos);
+ nPos--;
+ bRet = true;
+ }
+ }
+ if (nPos + 1 < mvData.size())
+ {
+ if (mvData[nPos + 1].pPattern == mvData[nPos].pPattern)
+ {
+ mvData[nPos].nEndRow = mvData[nPos + 1].nEndRow;
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ mvData.erase(mvData.begin() + nPos + 1);
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+/*
+ * nCount is the number of runs of different attribute combinations;
+ * no attribute in a column => nCount==1, one attribute somewhere => nCount == 3
+ * (ie. one run with no attribute + one attribute + another run with no attribute)
+ * so a range of identical attributes is only one entry in ScAttrArray.
+ *
+ * Iterative implementation of Binary Search
+ * The same implementation was used inside ScMarkArray::Search().
+ */
+
+bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const
+{
+/* auto it = std::lower_bound(mvData.begin(), mvData.end(), nRow,
+ [] (const ScAttrEntry &r1, SCROW nRow)
+ { return r1.nEndRow < nRow; } );
+ if (it != mvData.end())
+ nIndex = it - mvData.begin();
+ return it != mvData.end(); */
+
+ if (mvData.size() == 1)
+ {
+ nIndex = 0;
+ return true;
+ }
+
+ tools::Long nHi = static_cast<tools::Long>(mvData.size()) - 1;
+ tools::Long i = 0;
+ tools::Long nLo = 0;
+
+ while ( nLo <= nHi )
+ {
+ i = (nLo + nHi) / 2;
+
+ if (mvData[i].nEndRow < nRow)
+ {
+ // If [nRow] greater, ignore left half
+ nLo = i + 1;
+ }
+ else if ((i > 0) && (mvData[i - 1].nEndRow >= nRow))
+ {
+ // If [nRow] is smaller, ignore right half
+ nHi = i - 1;
+ }
+ else
+ {
+ // found
+ nIndex=static_cast<SCSIZE>(i);
+ return true;
+ }
+ }
+
+ nIndex=0;
+ return false;
+}
+
+const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const
+{
+ if ( mvData.empty() )
+ {
+ if ( !rDocument.ValidRow(nRow) )
+ return nullptr;
+ return rDocument.GetDefPattern();
+ }
+ SCSIZE i;
+ if (Search( nRow, i ))
+ return mvData[i].pPattern;
+ else
+ return nullptr;
+}
+
+const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow,
+ SCROW& rEndRow, SCROW nRow ) const
+{
+ if ( mvData.empty() )
+ {
+ if ( !rDocument.ValidRow( nRow ) )
+ return nullptr;
+ rStartRow = 0;
+ rEndRow = rDocument.MaxRow();
+ return rDocument.GetDefPattern();
+ }
+ SCSIZE nIndex;
+ if ( Search( nRow, nIndex ) )
+ {
+ if ( nIndex > 0 )
+ rStartRow = mvData[nIndex-1].nEndRow + 1;
+ else
+ rStartRow = 0;
+ rEndRow = mvData[nIndex].nEndRow;
+ return mvData[nIndex].pPattern;
+ }
+ return nullptr;
+}
+
+void ScAttrArray::AddCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
+{
+ if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
+ return;
+
+ if(nEndRow < nStartRow)
+ return;
+
+ SCROW nTempStartRow = nStartRow;
+ SCROW nTempEndRow = nEndRow;
+
+ do
+ {
+ const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
+
+ std::unique_ptr<ScPatternAttr> pNewPattern;
+ if(pPattern)
+ {
+ pNewPattern.reset( new ScPatternAttr(*pPattern) );
+ SCROW nPatternStartRow;
+ SCROW nPatternEndRow;
+ GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
+
+ nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
+ if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
+ {
+ ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
+ if (rCondFormatData.find(nIndex) == rCondFormatData.end())
+ {
+ ScCondFormatIndexes aNewCondFormatData;
+ aNewCondFormatData.reserve(rCondFormatData.size()+1);
+ aNewCondFormatData = rCondFormatData;
+ aNewCondFormatData.insert(nIndex);
+ ScCondFormatItem aItem( std::move(aNewCondFormatData) );
+ pNewPattern->GetItemSet().Put( aItem );
+ }
+ }
+ else
+ {
+ ScCondFormatItem aItem(nIndex);
+ pNewPattern->GetItemSet().Put( aItem );
+ }
+ }
+ else
+ {
+ pNewPattern.reset( new ScPatternAttr( rDocument.GetPool() ) );
+ ScCondFormatItem aItem(nIndex);
+ pNewPattern->GetItemSet().Put( aItem );
+ nTempEndRow = nEndRow;
+ }
+
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pNewPattern), true );
+ nTempStartRow = nTempEndRow + 1;
+ }
+ while(nTempEndRow < nEndRow);
+
+}
+
+void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex )
+{
+ if(!rDocument.ValidRow(nStartRow) || !rDocument.ValidRow(nEndRow))
+ return;
+
+ if(nEndRow < nStartRow)
+ return;
+
+ SCROW nTempStartRow = nStartRow;
+ SCROW nTempEndRow = nEndRow;
+
+ do
+ {
+ const ScPatternAttr* pPattern = GetPattern(nTempStartRow);
+
+ if(pPattern)
+ {
+ SCROW nPatternStartRow;
+ SCROW nPatternEndRow;
+ GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow );
+
+ nTempEndRow = std::min<SCROW>( nPatternEndRow, nEndRow );
+ if (const ScCondFormatItem* pItem = pPattern->GetItemSet().GetItemIfSet( ATTR_CONDITIONAL ))
+ {
+ auto pPatternAttr = std::make_unique<ScPatternAttr>( *pPattern );
+ if (nIndex == 0)
+ {
+ ScCondFormatItem aItem;
+ pPatternAttr->GetItemSet().Put( aItem );
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
+ }
+ else
+ {
+ ScCondFormatIndexes const & rCondFormatData = pItem->GetCondFormatData();
+ auto itr = rCondFormatData.find(nIndex);
+ if(itr != rCondFormatData.end())
+ {
+ ScCondFormatIndexes aNewCondFormatData(rCondFormatData);
+ aNewCondFormatData.erase_at(std::distance(rCondFormatData.begin(), itr));
+ ScCondFormatItem aItem( std::move(aNewCondFormatData) );
+ pPatternAttr->GetItemSet().Put( aItem );
+ SetPatternArea( nTempStartRow, nTempEndRow, std::move(pPatternAttr), true );
+ }
+ }
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ nTempStartRow = nTempEndRow + 1;
+ }
+ while(nTempEndRow < nEndRow);
+
+}
+
+void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow,
+ const ScPatternAttr* pPattern, ScEditDataArray* pDataArray )
+{
+ assert( nCol != -1 );
+ // cache mdds position, this doesn't modify the mdds container, just EditTextObject's
+ sc::ColumnBlockPosition blockPos;
+ rDocument.InitColumnBlockPosition( blockPos, nTab, nCol );
+ for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow)
+ {
+ ScAddress aPos(nCol, nRow, nTab);
+ ScRefCellValue aCell(rDocument, aPos, blockPos);
+ if (aCell.meType != CELLTYPE_EDIT || !aCell.mpEditText)
+ continue;
+
+ std::unique_ptr<EditTextObject> pOldData;
+ if (pDataArray)
+ pOldData = aCell.mpEditText->Clone();
+
+ // Direct modification of cell content - something to watch out for if
+ // we decide to share edit text instances in the future.
+ ScEditUtil::RemoveCharAttribs(const_cast<EditTextObject&>(*aCell.mpEditText), *pPattern);
+
+ if (pDataArray)
+ {
+ std::unique_ptr<EditTextObject> pNewData = aCell.mpEditText->Clone();
+ pDataArray->AddItem(nTab, nCol, nRow, std::move(pOldData), std::move(pNewData));
+ }
+ }
+}
+
+bool ScAttrArray::Reserve( SCSIZE nReserve )
+{
+ if ( mvData.empty() && nReserve )
+ {
+ try {
+ mvData.reserve(nReserve);
+ mvData.emplace_back();
+ mvData[0].nEndRow = rDocument.MaxRow();
+ mvData[0].pPattern = rDocument.GetDefPattern(); // no put
+ return true;
+ } catch (std::bad_alloc const &) {
+ return false;
+ }
+ }
+ else if ( mvData.capacity() < nReserve )
+ {
+ try {
+ mvData.reserve(nReserve);
+ return true;
+ } catch (std::bad_alloc const &) {
+ return false;
+ }
+ }
+ else
+ return false;
+}
+
+const ScPatternAttr* ScAttrArray::SetPatternAreaImpl(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern,
+ bool bPutToPool, ScEditDataArray* pDataArray, bool bPassingOwnership )
+{
+ if (rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow))
+ {
+ if (bPutToPool)
+ {
+ if (bPassingOwnership)
+ pPattern = &rDocument.GetPool()->Put(std::unique_ptr<ScPatternAttr>(const_cast<ScPatternAttr*>(pPattern)));
+ else
+ pPattern = &rDocument.GetPool()->Put(*pPattern);
+ }
+ if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
+ Reset(pPattern);
+ else
+ {
+ SCSIZE nNeeded = mvData.size() + 2;
+ SetDefaultIfNotInit( nNeeded );
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ SCSIZE ni = 0; // number of entries in beginning
+ SCSIZE nx = 0; // track position
+ SCROW ns = 0; // start row of track position
+ if ( nStartRow > 0 )
+ {
+ // skip beginning
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ ni = nIndex;
+
+ if ( ni > 0 )
+ {
+ nx = ni;
+ ns = mvData[ni-1].nEndRow+1;
+ }
+ }
+
+ // ensure that attributing changes text width of cell
+ // otherwise, conditional formats need to be reset or deleted
+ bool bIsLoading = !rDocument.GetDocumentShell() || rDocument.GetDocumentShell()->IsLoading();
+ while ( ns <= nEndRow )
+ {
+ if ( nCol != -1 && !bIsLoading )
+ {
+ const SfxItemSet& rNewSet = pPattern->GetItemSet();
+ const SfxItemSet& rOldSet = mvData[nx].pPattern->GetItemSet();
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( std::max(nStartRow,ns) );
+ aAdrEnd .SetRow( std::min(nEndRow,mvData[nx].nEndRow) );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+ ns = mvData[nx].nEndRow + 1;
+ nx++;
+ }
+
+ // continue modifying data array
+
+ SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert)
+ bool bCombined = false;
+ bool bSplit = false;
+ if ( nStartRow > 0 )
+ {
+ nInsert = rDocument.MaxRow() + 1;
+ if ( mvData[ni].pPattern != pPattern )
+ {
+ if ( ni == 0 || (mvData[ni-1].nEndRow < nStartRow - 1) )
+ { // may be a split or a simple insert or just a shrink,
+ // row adjustment is done further down
+ if ( mvData[ni].nEndRow > nEndRow )
+ bSplit = true;
+ ni++;
+ nInsert = ni;
+ }
+ else if (mvData[ni - 1].nEndRow == nStartRow - 1)
+ nInsert = ni;
+ }
+ if ( ni > 0 && mvData[ni-1].pPattern == pPattern )
+ { // combine
+ mvData[ni-1].nEndRow = nEndRow;
+ nInsert = rDocument.MaxRow() + 1;
+ bCombined = true;
+ }
+ }
+ else
+ nInsert = 0;
+
+ SCSIZE nj = ni; // stop position of range to replace
+ while ( nj < mvData.size() && mvData[nj].nEndRow <= nEndRow )
+ nj++;
+ if ( !bSplit )
+ {
+ if ( nj < mvData.size() && mvData[nj].pPattern == pPattern )
+ { // combine
+ if ( ni > 0 )
+ {
+ if ( mvData[ni-1].pPattern == pPattern )
+ { // adjacent entries
+ mvData[ni-1].nEndRow = mvData[nj].nEndRow;
+ nj++;
+ }
+ else if ( ni == nInsert )
+ mvData[ni-1].nEndRow = nStartRow - 1; // shrink
+ }
+ nInsert = rDocument.MaxRow() + 1;
+ bCombined = true;
+ }
+ else if ( ni > 0 && ni == nInsert )
+ mvData[ni-1].nEndRow = nStartRow - 1; // shrink
+ }
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ if ( bSplit )
+ { // duplicate split entry in pool
+ pDocPool->Put( *mvData[ni-1].pPattern );
+ }
+ if ( ni < nj )
+ { // remove middle entries
+ for ( SCSIZE nk=ni; nk<nj; nk++)
+ { // remove entries from pool
+ pDocPool->Remove( *mvData[nk].pPattern );
+ }
+ if ( !bCombined )
+ { // replace one entry
+ mvData[ni].nEndRow = nEndRow;
+ mvData[ni].pPattern = pPattern;
+ ni++;
+ nInsert = rDocument.MaxRow() + 1;
+ }
+ if ( ni < nj )
+ { // remove entries
+ mvData.erase( mvData.begin() + ni, mvData.begin() + nj);
+ }
+ }
+
+ if ( nInsert < sal::static_int_cast<SCSIZE>(rDocument.MaxRow() + 1) )
+ { // insert or append new entry
+ if ( nInsert <= mvData.size() )
+ {
+ if ( !bSplit )
+ mvData.emplace(mvData.begin() + nInsert);
+ else
+ {
+ mvData.insert(mvData.begin() + nInsert, 2, ScAttrEntry());
+ mvData[nInsert+1] = mvData[nInsert-1];
+ }
+ }
+ if ( nInsert )
+ mvData[nInsert-1].nEndRow = nStartRow - 1;
+ mvData[nInsert].nEndRow = nEndRow;
+ mvData[nInsert].pPattern = pPattern;
+
+ // Remove character attributes from these cells if the pattern
+ // is applied during normal session.
+ if (pDataArray && nCol != -1)
+ RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray);
+ }
+
+ rDocument.SetStreamValid(nTab, false);
+ }
+ }
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+ return pPattern;
+}
+
+void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle )
+{
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SetDefaultIfNotInit();
+ SCSIZE nPos;
+ SCROW nStart=0;
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search Failure");
+ return;
+ }
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ do
+ {
+ const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
+ pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(&rStyle));
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if ( *pNewPattern == *pOldPattern )
+ {
+ // keep the original pattern (might be default)
+ // pNewPattern is deleted below
+ nPos++;
+ }
+ else if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ if ( nCol != -1 )
+ {
+ // ensure attributing changes text width of cell; otherwise
+ // there aren't (yet) template format changes
+ const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[nPos].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern = &rDocument.GetPool()->Put(*pNewPattern);
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ nPos++;
+ }
+ }
+ while ((nStart <= nEndRow) && (nPos < mvData.size()));
+
+ rDocument.SetStreamValid(nTab, false);
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+}
+
+ // const cast, otherwise it will be too inefficient/complicated
+static void SetLineColor(SvxBorderLine const * dest, Color c)
+{
+ if (dest)
+ {
+ const_cast<SvxBorderLine*>(dest)->SetColor(c);
+ }
+}
+
+static void SetLine(const SvxBorderLine* dest, const SvxBorderLine* src)
+{
+ if (dest)
+ {
+ SvxBorderLine* pCast = const_cast<SvxBorderLine*>(dest);
+ pCast->SetBorderLineStyle( src->GetBorderLineStyle() );
+ pCast->SetWidth( src->GetWidth() );
+ }
+}
+
+void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos;
+ SCROW nStart=0;
+ SetDefaultIfNotInit();
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search failure");
+ return;
+ }
+
+ do
+ {
+ const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+ const SvxBoxItem* pBoxItem = rOldSet.GetItemIfSet( ATTR_BORDER );
+ const SvxLineItem* pTLBRItem = rOldSet.GetItemIfSet( ATTR_BORDER_TLBR );
+ const SvxLineItem* pBLTRItem = rOldSet.GetItemIfSet( ATTR_BORDER_BLTR );
+
+ if ( pBoxItem || pTLBRItem || pBLTRItem )
+ {
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pOldPattern));
+ SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+
+ std::unique_ptr<SvxBoxItem> pNewBoxItem( pBoxItem ? pBoxItem->Clone() : nullptr);
+ std::unique_ptr<SvxLineItem> pNewTLBRItem( pTLBRItem ? pTLBRItem->Clone() : nullptr);
+ std::unique_ptr<SvxLineItem> pNewBLTRItem(pBLTRItem ? pBLTRItem->Clone() : nullptr);
+
+ // fetch line and update attributes with parameters
+
+ if ( !pLine )
+ {
+ if( pNewBoxItem )
+ {
+ if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::TOP );
+ if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::LEFT );
+ if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ }
+ if( pNewTLBRItem && pNewTLBRItem->GetLine() )
+ pNewTLBRItem->SetLine( nullptr );
+ if( pNewBLTRItem && pNewBLTRItem->GetLine() )
+ pNewBLTRItem->SetLine( nullptr );
+ }
+ else
+ {
+ if ( bColorOnly )
+ {
+ Color aColor( pLine->GetColor() );
+ if( pNewBoxItem )
+ {
+ SetLineColor( pNewBoxItem->GetTop(), aColor );
+ SetLineColor( pNewBoxItem->GetBottom(), aColor );
+ SetLineColor( pNewBoxItem->GetLeft(), aColor );
+ SetLineColor( pNewBoxItem->GetRight(), aColor );
+ }
+ if( pNewTLBRItem )
+ SetLineColor( pNewTLBRItem->GetLine(), aColor );
+ if( pNewBLTRItem )
+ SetLineColor( pNewBLTRItem->GetLine(), aColor );
+ }
+ else
+ {
+ if( pNewBoxItem )
+ {
+ SetLine( pNewBoxItem->GetTop(), pLine );
+ SetLine( pNewBoxItem->GetBottom(), pLine );
+ SetLine( pNewBoxItem->GetLeft(), pLine );
+ SetLine( pNewBoxItem->GetRight(), pLine );
+ }
+ if( pNewTLBRItem )
+ SetLine( pNewTLBRItem->GetLine(), pLine );
+ if( pNewBLTRItem )
+ SetLine( pNewBLTRItem->GetLine(), pLine );
+ }
+ }
+ if( pNewBoxItem ) rNewSet.Put( std::move(pNewBoxItem) );
+ if( pNewTLBRItem ) rNewSet.Put( std::move(pNewTLBRItem) );
+ if( pNewBLTRItem ) rNewSet.Put( std::move(pNewBLTRItem) );
+
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, std::move(pNewPattern), true );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ // remove from pool ?
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern =
+ &rDocument.GetPool()->Put(std::move(pNewPattern));
+
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ nPos++;
+ }
+ }
+ else
+ {
+ nStart = mvData[nPos].nEndRow + 1;
+ nPos++;
+ }
+ }
+ while ((nStart <= nEndRow) && (nPos < mvData.size()));
+}
+
+void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache, ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos;
+ SCROW nStart=0;
+ SetDefaultIfNotInit();
+ if (!Search( nStartRow, nPos ))
+ {
+ OSL_FAIL("Search Failure");
+ return;
+ }
+
+ ScAddress aAdrStart( nCol, 0, nTab );
+ ScAddress aAdrEnd ( nCol, 0, nTab );
+
+ do
+ {
+ const ScPatternAttr* pOldPattern = mvData[nPos].pPattern;
+ const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>( &pCache->ApplyTo( *pOldPattern ) );
+ if (pNewPattern != pOldPattern)
+ {
+ SCROW nY1 = nStart;
+ SCROW nY2 = mvData[nPos].nEndRow;
+ nStart = mvData[nPos].nEndRow + 1;
+
+ if(pIsChanged)
+ *pIsChanged = true;
+
+ if ( nY1 < nStartRow || nY2 > nEndRow )
+ {
+ if (nY1 < nStartRow) nY1=nStartRow;
+ if (nY2 > nEndRow) nY2=nEndRow;
+ SetPatternArea( nY1, nY2, pNewPattern, false, pDataArray );
+ Search( nStart, nPos );
+ }
+ else
+ {
+ if ( nCol != -1 )
+ {
+ // ensure attributing changes text-width of cell
+
+ const SfxItemSet& rNewSet = pNewPattern->GetItemSet();
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+
+ bool bNumFormatChanged;
+ if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged,
+ rNewSet, rOldSet ) )
+ {
+ aAdrStart.SetRow( nPos ? mvData[nPos-1].nEndRow+1 : 0 );
+ aAdrEnd .SetRow( mvData[nPos].nEndRow );
+ rDocument.InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged );
+ }
+ }
+
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ mvData[nPos].pPattern = pNewPattern;
+ if (Concat(nPos))
+ Search(nStart, nPos);
+ else
+ ++nPos;
+ }
+ }
+ else
+ {
+ nStart = mvData[nPos].nEndRow + 1;
+ ++nPos;
+ }
+ }
+ while (nStart <= nEndRow);
+
+ rDocument.SetStreamValid(nTab, false);
+
+#if DEBUG_SC_TESTATTRARRAY
+ TestData();
+#endif
+}
+
+void ScAttrArray::SetAttrEntries(std::vector<ScAttrEntry> && vNewData)
+{
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (auto const & rEntry : mvData)
+ 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 )
+{
+ const SfxPoolItem* pNewItem;
+ const SfxPoolItem* pOldItem;
+ for (sal_uInt16 nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++)
+ {
+ // pMergeSet has no parent
+ SfxItemState eOldState = rMergeSet.GetItemState( nId, false, &pOldItem );
+
+ if ( eOldState == SfxItemState::DEFAULT )
+ {
+ SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
+ if ( eNewState == SfxItemState::SET )
+ {
+ if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) )
+ rMergeSet.InvalidateItem( nId );
+ }
+ }
+ else if ( eOldState == SfxItemState::SET ) // Item set
+ {
+ SfxItemState eNewState = rSource.GetItemState( nId, true, &pNewItem );
+ if ( eNewState == SfxItemState::SET )
+ {
+ if ( pNewItem != pOldItem ) // Both pulled
+ rMergeSet.InvalidateItem( nId );
+ }
+ else // Default
+ {
+ if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) )
+ rMergeSet.InvalidateItem( nId );
+ }
+ }
+ // Dontcare remains Dontcare
+ }
+}
+
+void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow,
+ ScMergePatternState& rState, bool bDeep ) const
+{
+ if (!(rDocument.ValidRow(nStartRow) && rDocument.ValidRow(nEndRow)))
+ return;
+
+ SCSIZE nPos = 0;
+ SCROW nStart=0;
+ if ( !mvData.empty() && !Search( nStartRow, nPos ) )
+ {
+ OSL_FAIL("Search failure");
+ return;
+ }
+
+ do
+ {
+ // similar patterns must not be repeated
+ const ScPatternAttr* pPattern = nullptr;
+ if ( !mvData.empty() )
+ pPattern = mvData[nPos].pPattern;
+ else
+ pPattern = rDocument.GetDefPattern();
+ if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 )
+ {
+ const SfxItemSet& rThisSet = pPattern->GetItemSet();
+ if (rState.pItemSet)
+ {
+ rState.mbValidPatternId = false;
+ if (bDeep)
+ lcl_MergeDeep( *rState.pItemSet, rThisSet );
+ else
+ rState.pItemSet->MergeValues( rThisSet );
+ }
+ else
+ {
+ // first pattern - copied from parent
+ rState.pItemSet.emplace( *rThisSet.GetPool(), rThisSet.GetRanges() );
+ rState.pItemSet->Set( rThisSet, bDeep );
+ rState.mnPatternId = pPattern->GetKey();
+ }
+
+ rState.pOld2 = rState.pOld1;
+ rState.pOld1 = pPattern;
+ }
+
+ if ( !mvData.empty() )
+ nStart = mvData[nPos].nEndRow + 1;
+ else
+ nStart = rDocument.MaxRow() + 1;
+ ++nPos;
+ }
+ while (nStart <= nEndRow);
+}
+
+// assemble border
+
+static bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
+ sal_uInt8& rModified, const SvxBorderLine*& rpNew )
+{
+ if (rModified == SC_LINE_DONTCARE)
+ return false; // don't go again
+
+ if (rModified == SC_LINE_EMPTY)
+ {
+ rModified = SC_LINE_SET;
+ rpNew = pNewLine;
+ return true; // initial value
+ }
+
+ if (pOldLine == pNewLine)
+ {
+ rpNew = pOldLine;
+ return false;
+ }
+
+ if (pOldLine && pNewLine)
+ if (*pOldLine == *pNewLine)
+ {
+ rpNew = pOldLine;
+ return false;
+ }
+
+ rModified = SC_LINE_DONTCARE;
+ rpNew = nullptr;
+ return true; // another line -> don't care
+}
+
+static void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
+ ScLineFlags& rFlags, const ScPatternAttr* pPattern,
+ bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
+{
+ // right/bottom border set when connected together
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if ( rMerge.GetColMerge() == nDistRight + 1 )
+ nDistRight = 0;
+ if ( rMerge.GetRowMerge() == nDistBottom + 1 )
+ nDistBottom = 0;
+
+ const SvxBoxItem* pCellFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
+ const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft();
+ const SvxBorderLine* pRightAttr = pCellFrame->GetRight();
+ const SvxBorderLine* pTopAttr = pCellFrame->GetTop();
+ const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom();
+ const SvxBorderLine* pNew;
+
+ if (bTop)
+ {
+ if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::TOP );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
+ }
+
+ if (nDistBottom == 0)
+ {
+ if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::BOTTOM );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::HORI );
+ }
+
+ if (bLeft)
+ {
+ if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::LEFT );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
+ }
+
+ if (nDistRight == 0)
+ {
+ if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew ))
+ pLineOuter->SetLine( pNew, SvxBoxItemLine::RIGHT );
+ }
+ else
+ {
+ if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew ))
+ pLineInner->SetLine( pNew, SvxBoxInfoItemLine::VERT );
+ }
+}
+
+void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
+ ScLineFlags& rFlags,
+ SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight ) const
+{
+ const ScPatternAttr* pPattern;
+
+ if (nStartRow == nEndRow)
+ {
+ pPattern = GetPattern( nStartRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true, 0 );
+ }
+ else if ( !mvData.empty() ) // non-default pattern
+ {
+ pPattern = GetPattern( nStartRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, true,
+ nEndRow-nStartRow );
+
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow+1, nStartIndex );
+ Search( nEndRow-1, nEndIndex );
+ for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
+ {
+ pPattern = mvData[i].pPattern;
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false,
+ nEndRow - std::min( mvData[i].nEndRow, static_cast<SCROW>(nEndRow-1) ) );
+ // nDistBottom here always > 0
+ }
+
+ pPattern = GetPattern( nEndRow );
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 );
+ }
+ else
+ {
+ lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, rDocument.GetDefPattern(), bLeft, nDistRight, true, 0 );
+ }
+}
+
+// apply border
+
+// ApplyFrame - on an entry into the array
+
+bool ScAttrArray::ApplyFrame( const SvxBoxItem& rBoxItem,
+ const SvxBoxInfoItem* pBoxInfoItem,
+ SCROW nStartRow, SCROW nEndRow,
+ bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom )
+{
+ OSL_ENSURE( pBoxInfoItem, "Missing line attributes!" );
+
+ const ScPatternAttr* pPattern = GetPattern( nStartRow );
+ const SvxBoxItem* pOldFrame = &pPattern->GetItemSet().Get( ATTR_BORDER );
+
+ // right/bottom border set when connected together
+ const ScMergeAttr& rMerge = pPattern->GetItem(ATTR_MERGE);
+ if ( rMerge.GetColMerge() == nDistRight + 1 )
+ nDistRight = 0;
+ if ( rMerge.GetRowMerge() == nDistBottom + 1 )
+ nDistBottom = 0;
+
+ SvxBoxItem aNewFrame( *pOldFrame );
+ bool bRTL=rDocument.IsLayoutRTL(nTab);
+ // fdo#37464 check if the sheet are RTL then replace right <=> left
+ if (bRTL)
+ {
+ if( bLeft && nDistRight==0)
+ {
+ if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) )
+ aNewFrame.SetLine( rBoxItem.GetLeft(), SvxBoxItemLine::RIGHT );
+ if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) )
+ aNewFrame.SetLine( rBoxItem.GetRight(), SvxBoxItemLine::LEFT );
+ }
+ else
+ {
+ if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::RIGHT );
+ if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( bLeft ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::LEFT );
+ }
+ }
+ else
+ {
+ if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( bLeft ? rBoxItem.GetLeft() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::LEFT );
+ if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) )
+ aNewFrame.SetLine( (nDistRight==0) ? rBoxItem.GetRight() : pBoxInfoItem->GetVert(),
+ SvxBoxItemLine::RIGHT );
+ }
+ if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ aNewFrame.SetLine( bTop ? rBoxItem.GetTop() : pBoxInfoItem->GetHori(),
+ SvxBoxItemLine::TOP );
+ if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) )
+ aNewFrame.SetLine( (nDistBottom==0) ? rBoxItem.GetBottom() : pBoxInfoItem->GetHori(),
+ SvxBoxItemLine::BOTTOM );
+
+ if (aNewFrame == *pOldFrame)
+ {
+ // nothing to do
+ return false;
+ }
+ else
+ {
+ SfxItemPoolCache aCache( rDocument.GetPool(), &aNewFrame );
+ ApplyCacheArea( nStartRow, nEndRow, &aCache );
+
+ return true;
+ }
+}
+
+void ScAttrArray::ApplyBlockFrame(const SvxBoxItem& rLineOuter, const SvxBoxInfoItem* pLineInner,
+ SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight)
+{
+ SetDefaultIfNotInit();
+ if (nStartRow == nEndRow)
+ ApplyFrame(rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0);
+ else
+ {
+ ApplyFrame(rLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight,
+ true, nEndRow-nStartRow);
+
+ if ( nEndRow > nStartRow+1 ) // inner part available?
+ {
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow+1, nStartIndex );
+ Search( nEndRow-1, nEndIndex );
+ SCROW nTmpStart = nStartRow+1;
+ SCROW nTmpEnd;
+ for (SCSIZE i=nStartIndex; i<=nEndIndex;)
+ {
+ nTmpEnd = std::min( static_cast<SCROW>(nEndRow-1), mvData[i].nEndRow );
+ bool bChanged = ApplyFrame(rLineOuter, pLineInner, nTmpStart, nTmpEnd,
+ bLeft, nDistRight, false, nEndRow - nTmpEnd);
+ nTmpStart = nTmpEnd+1;
+ if (bChanged)
+ {
+ Search(nTmpStart, i);
+ Search(nEndRow-1, nEndIndex);
+ }
+ else
+ i++;
+ }
+ }
+
+ ApplyFrame(rLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0);
+ }
+}
+
+bool ScAttrArray::HasAttrib_Impl(const ScPatternAttr* pPattern, HasAttrFlags nMask, SCROW nRow1, SCROW nRow2, SCSIZE i) const
+{
+ bool bFound = false;
+ if ( nMask & HasAttrFlags::Merged )
+ {
+ const ScMergeAttr* pMerge = &pPattern->GetItem( ATTR_MERGE );
+ if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 )
+ bFound = true;
+ }
+ if ( nMask & ( HasAttrFlags::Overlapped | HasAttrFlags::NotOverlapped | HasAttrFlags::AutoFilter ) )
+ {
+ const ScMergeFlagAttr* pMergeFlag = &pPattern->GetItem( ATTR_MERGE_FLAG );
+ if ( (nMask & HasAttrFlags::Overlapped) && pMergeFlag->IsOverlapped() )
+ bFound = true;
+ if ( (nMask & HasAttrFlags::NotOverlapped) && !pMergeFlag->IsOverlapped() )
+ bFound = true;
+ if ( (nMask & HasAttrFlags::AutoFilter) && pMergeFlag->HasAutoFilter() )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Lines )
+ {
+ const SvxBoxItem* pBox = &pPattern->GetItem( ATTR_BORDER );
+ if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Shadow )
+ {
+ const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
+ if ( pShadow->GetLocation() != SvxShadowLocation::NONE )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Conditional )
+ {
+ if ( !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty())
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Protected )
+ {
+ const ScProtectionAttr* pProtect = &pPattern->GetItem( ATTR_PROTECTION );
+ bool bFoundTemp = false;
+ if ( pProtect->GetProtection() || pProtect->GetHideCell() )
+ bFoundTemp = true;
+
+ bool bContainsCondFormat = !mvData.empty() &&
+ !pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty();
+ if ( bContainsCondFormat && nCol != -1 ) // rDocument.GetCondResult() is valid only for real columns.
+ {
+ SCROW nRowStartCond = std::max<SCROW>( nRow1, i ? mvData[i-1].nEndRow + 1: 0 );
+ SCROW nRowEndCond = std::min<SCROW>( nRow2, mvData[i].nEndRow );
+ bool bFoundCond = false;
+ for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond)
+ {
+ const SfxItemSet* pSet = rDocument.GetCondResult( nCol, nRowCond, nTab );
+
+ const ScProtectionAttr* pCondProtect;
+ if( pSet && (pCondProtect = pSet->GetItemIfSet( ATTR_PROTECTION )) )
+ {
+ if( pCondProtect->GetProtection() || pCondProtect->GetHideCell() )
+ bFoundCond = true;
+ else
+ break;
+ }
+ else
+ {
+ // well it is not true that we found one
+ // but existing one + cell where conditional
+ // formatting does not remove it
+ // => we should use the existing protection setting
+ bFoundCond = bFoundTemp;
+ }
+ }
+ bFoundTemp = bFoundCond;
+ }
+
+ if(bFoundTemp)
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::Rotate )
+ {
+ const ScRotateValueItem* pRotate = &pPattern->GetItem( ATTR_ROTATE_VALUE );
+ // 90 or 270 degrees is former SvxOrientationItem - only look for other values
+ // (see ScPatternAttr::GetCellOrientation)
+ Degree100 nAngle = pRotate->GetValue();
+ if ( nAngle && nAngle != 9000_deg100 && nAngle != 27000_deg100 )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::NeedHeight )
+ {
+ if (pPattern->GetCellOrientation() != SvxCellOrientation::Standard)
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_LINEBREAK ).GetValue())
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() == SvxCellHorJustify::Block)
+ bFound = true;
+
+ else if (!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
+ bFound = true;
+ else if (pPattern->GetItem( ATTR_ROTATE_VALUE ).GetValue())
+ bFound = true;
+ }
+ if ( nMask & ( HasAttrFlags::ShadowRight | HasAttrFlags::ShadowDown ) )
+ {
+ const SvxShadowItem* pShadow = &pPattern->GetItem( ATTR_SHADOW );
+ SvxShadowLocation eLoc = pShadow->GetLocation();
+ if ( nMask & HasAttrFlags::ShadowRight )
+ if ( eLoc == SvxShadowLocation::TopRight || eLoc == SvxShadowLocation::BottomRight )
+ bFound = true;
+ if ( nMask & HasAttrFlags::ShadowDown )
+ if ( eLoc == SvxShadowLocation::BottomLeft || eLoc == SvxShadowLocation::BottomRight )
+ bFound = true;
+ }
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // called only if the sheet is LTR, so physical=logical alignment can be assumed
+ SvxCellHorJustify eHorJust = pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue();
+ if ( eHorJust == SvxCellHorJustify::Right || eHorJust == SvxCellHorJustify::Center )
+ bFound = true;
+ }
+
+ return bFound;
+}
+
+// Test if field contains specific attribute
+bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, HasAttrFlags nMask ) const
+{
+ if (mvData.empty())
+ {
+ return HasAttrib_Impl(rDocument.GetDefPattern(), nMask, 0, rDocument.MaxRow(), 0);
+ }
+
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nRow1, nStartIndex );
+ if (nRow1 != nRow2)
+ Search( nRow2, nEndIndex );
+ else
+ nEndIndex = nStartIndex;
+ bool bFound = false;
+
+ for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++)
+ {
+ const ScPatternAttr* pPattern = mvData[i].pPattern;
+ bFound = HasAttrib_Impl(pPattern, nMask, nRow1, nRow2, i);
+ }
+
+ return bFound;
+}
+
+bool ScAttrArray::HasAttrib( SCROW nRow, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ if (mvData.empty())
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = rDocument.MaxRow();
+ return HasAttrib_Impl(rDocument.GetDefPattern(), nMask, 0, rDocument.MaxRow(), 0);
+ }
+
+ SCSIZE nIndex;
+ Search( nRow, nIndex );
+ if( nStartRow )
+ *nStartRow = nIndex > 0 ? mvData[nIndex-1].nEndRow+1 : 0;
+ if( nEndRow )
+ *nEndRow = mvData[nIndex].nEndRow;
+ const ScPatternAttr* pPattern = mvData[nIndex].pPattern;
+ return HasAttrib_Impl(pPattern, nMask, nRow, nRow, nIndex);
+}
+
+bool ScAttrArray::IsMerged( SCROW nRow ) const
+{
+ if ( !mvData.empty() )
+ {
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ const ScMergeAttr& rItem = mvData[nIndex].pPattern->GetItem(ATTR_MERGE);
+
+ return rItem.IsMerged();
+ }
+
+ return rDocument.GetDefPattern()->GetItem(ATTR_MERGE).IsMerged();
+}
+
+/**
+ * Area around any given summaries expand and adapt any MergeFlag (bRefresh)
+ */
+bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
+ SCCOL& rPaintCol, SCROW& rPaintRow,
+ bool bRefresh )
+{
+ assert( nCol != -1 );
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pPattern;
+ const ScMergeAttr* pItem;
+ SCSIZE nStartIndex;
+ SCSIZE nEndIndex;
+ Search( nStartRow, nStartIndex );
+ Search( nEndRow, nEndIndex );
+ bool bFound = false;
+
+ for (SCSIZE i=nStartIndex; i<=nEndIndex; i++)
+ {
+ pPattern = mvData[i].pPattern;
+ pItem = &pPattern->GetItem( ATTR_MERGE );
+ SCCOL nCountX = pItem->GetColMerge();
+ SCROW nCountY = pItem->GetRowMerge();
+ if (nCountX>1 || nCountY>1)
+ {
+ SCROW nThisRow = (i>0) ? mvData[i-1].nEndRow+1 : 0;
+ SCCOL nMergeEndCol = nThisCol + nCountX - 1;
+ SCROW nMergeEndRow = nThisRow + nCountY - 1;
+ if (nMergeEndCol > rPaintCol && nMergeEndCol <= rDocument.MaxCol())
+ rPaintCol = nMergeEndCol;
+ if (nMergeEndRow > rPaintRow && nMergeEndRow <= rDocument.MaxRow())
+ rPaintRow = nMergeEndRow;
+ bFound = true;
+
+ if (bRefresh)
+ {
+ if ( nMergeEndCol > nThisCol )
+ rDocument.ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, mvData[i].nEndRow,
+ nTab, ScMF::Hor );
+ if ( nMergeEndRow > nThisRow )
+ rDocument.ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow,
+ nTab, ScMF::Ver );
+ if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow )
+ rDocument.ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow,
+ nTab, ScMF::Hor | ScMF::Ver );
+
+ Search( nThisRow, i ); // Data changed
+ Search( nStartRow, nStartIndex );
+ Search( nEndRow, nEndIndex );
+ }
+ }
+ }
+
+ return bFound;
+}
+
+void ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
+{
+ assert( nCol != -1 );
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pPattern;
+ const ScMergeAttr* pItem;
+ SCSIZE nIndex;
+
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisStart < nStartRow)
+ nThisStart = nStartRow;
+
+ while ( nThisStart <= nEndRow )
+ {
+ SCROW nThisEnd = mvData[nIndex].nEndRow;
+ if (nThisEnd > nEndRow)
+ nThisEnd = nEndRow;
+
+ pPattern = mvData[nIndex].pPattern;
+ pItem = &pPattern->GetItem( ATTR_MERGE );
+ SCCOL nCountX = pItem->GetColMerge();
+ SCROW nCountY = pItem->GetRowMerge();
+ if (nCountX>1 || nCountY>1)
+ {
+ const ScMergeAttr* pAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
+ const ScMergeFlagAttr* pFlagAttr = &rDocument.GetPool()->GetDefaultItem( ATTR_MERGE_FLAG );
+
+ OSL_ENSURE( nCountY==1 || nThisStart==nThisEnd, "What's up?" );
+
+ SCCOL nThisCol = nCol;
+ SCCOL nMergeEndCol = nThisCol + nCountX - 1;
+ SCROW nMergeEndRow = nThisEnd + nCountY - 1;
+
+ // ApplyAttr for areas
+ for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++)
+ rDocument.ApplyAttr( nThisCol, nThisRow, nTab, *pAttr );
+
+ ScPatternAttr aNewPattern( rDocument.GetPool() );
+ SfxItemSet* pSet = &aNewPattern.GetItemSet();
+ pSet->Put( *pFlagAttr );
+ rDocument.ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow,
+ nTab, aNewPattern );
+
+ Search( nThisEnd, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ if ( nIndex < mvData.size() )
+ nThisStart = mvData[nIndex-1].nEndRow+1;
+ else
+ nThisStart = rDocument.MaxRow()+1; // End
+ }
+}
+
+void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow,
+ const ScPatternAttr* pWantedPattern, bool bDefault )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+ const ScMergeFlagAttr* pItem;
+
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bFirstUse = true;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ if (pOldPattern != pWantedPattern) // FIXME: else-branch?
+ {
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ pItem = &pOldPattern->GetItem( ATTR_MERGE_FLAG );
+
+ if (pItem->IsOverlapped() || pItem->HasAutoFilter())
+ {
+ // default-constructing a ScPatternAttr for DeleteArea doesn't work
+ // because it would have no cell style information.
+ // Instead, the document's GetDefPattern is copied. Since it is passed as
+ // pWantedPattern, no special treatment of default is needed here anymore.
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pWantedPattern ));
+ pNewPattern->GetItemSet().Put( *pItem );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ }
+ else
+ {
+ if ( !bDefault )
+ {
+ if (bFirstUse)
+ bFirstUse = false;
+ else
+ // it's in the pool
+ rDocument.GetPool()->Put( *pWantedPattern );
+ }
+ SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
+ }
+
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+
+ ScMF nOldValue;
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bChanged = false;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
+ if ( (nOldValue | nFlags) != nOldValue )
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ bChanged = true;
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+
+ return bChanged;
+}
+
+bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, ScMF nFlags )
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pOldPattern;
+
+ ScMF nOldValue;
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+ bool bChanged = false;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ pOldPattern = mvData[nIndex].pPattern;
+ nOldValue = pOldPattern->GetItem( ATTR_MERGE_FLAG ).GetValue();
+ if ( (nOldValue & ~nFlags) != nOldValue )
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ bChanged = true;
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+
+ return bChanged;
+}
+
+void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
+{
+ SetDefaultIfNotInit();
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+ if ( pOldPattern->HasItemsSet( pWhich ) )
+ {
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->ClearItems( pWhich );
+
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, bool bIncrement )
+{
+ SetDefaultIfNotInit();
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisStart < nStartRow) nThisStart = nStartRow;
+
+ while ( nThisStart <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+ const SfxItemSet& rOldSet = pOldPattern->GetItemSet();
+ const SvxHorJustifyItem* pItem;
+
+ bool bNeedJust = !( pItem = rOldSet.GetItemIfSet( ATTR_HOR_JUSTIFY, false ) )
+ || (pItem->GetValue() != SvxCellHorJustify::Left &&
+ pItem->GetValue() != SvxCellHorJustify::Right );
+ sal_uInt16 nOldValue = rOldSet.Get( ATTR_INDENT ).GetValue();
+ sal_uInt16 nNewValue = nOldValue;
+ // To keep Increment indent from running outside the cell1659
+ tools::Long nColWidth = static_cast<tools::Long>(
+ rDocument.GetColWidth(nCol == -1 ? rDocument.MaxCol() : nCol,nTab));
+ if ( bIncrement )
+ {
+ if ( nNewValue < nColWidth-SC_INDENT_STEP )
+ {
+ nNewValue += SC_INDENT_STEP;
+ if ( nNewValue > nColWidth-SC_INDENT_STEP )
+ nNewValue = nColWidth-SC_INDENT_STEP;
+ }
+ }
+ else
+ {
+ if ( nNewValue > 0 )
+ {
+ if ( nNewValue > SC_INDENT_STEP )
+ nNewValue -= SC_INDENT_STEP;
+ else
+ nNewValue = 0;
+ }
+ }
+
+ if ( bNeedJust || nNewValue != nOldValue )
+ {
+ SCROW nThisEnd = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nThisEnd, nEndRow );
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ pNewPattern->GetItemSet().Put( ScIndentItem( nNewValue ) );
+ if ( bNeedJust )
+ pNewPattern->GetItemSet().Put(
+ SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) );
+ SetPatternArea( nThisStart, nAttrRow, std::move(pNewPattern), true );
+
+ nThisStart = nThisEnd + 1;
+ Search( nThisStart, nIndex ); // data changed
+ }
+ else
+ {
+ nThisStart = mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+ }
+}
+
+SCROW ScAttrArray::GetNextUnprotected( SCROW nRow, bool bUp ) const
+{
+ tools::Long nRet = nRow;
+ if (rDocument.ValidRow(nRow))
+ {
+ if ( mvData.empty() )
+ {
+ if ( bUp )
+ return -1;
+ else
+ return rDocument.MaxRow()+1;
+ }
+
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ while (mvData[nIndex].pPattern->
+ GetItem(ATTR_PROTECTION).GetProtection())
+ {
+ if (bUp)
+ {
+ if (nIndex==0)
+ return -1; // not found
+ --nIndex;
+ nRet = mvData[nIndex].nEndRow;
+ }
+ else
+ {
+ nRet = mvData[nIndex].nEndRow+1;
+ ++nIndex;
+ if (nIndex >= mvData.size())
+ return rDocument.MaxRow()+1; // not found
+ }
+ }
+ }
+ return nRet;
+}
+
+void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
+{
+ SetDefaultIfNotInit();
+ SCROW nStart = 0;
+ SCSIZE nPos = 0;
+ while (nPos < mvData.size())
+ {
+ SCROW nEnd = mvData[nPos].nEndRow;
+ if (mvData[nPos].pPattern->GetStyleSheet() == pStyleSheet)
+ {
+ rUsedRows.setTrue(nStart, nEnd);
+
+ if (bReset)
+ {
+ ScPatternAttr aNewPattern(*mvData[nPos].pPattern);
+ rDocument.GetPool()->Remove(*mvData[nPos].pPattern);
+ aNewPattern.SetStyleSheet( static_cast<ScStyleSheet*>(
+ rDocument.GetStyleSheetPool()->
+ Find( ScResId(STR_STYLENAME_STANDARD),
+ SfxStyleFamily::Para,
+ SfxStyleSearchBits::Auto | SfxStyleSearchBits::ScStandard ) ) );
+ mvData[nPos].pPattern = &rDocument.GetPool()->Put(aNewPattern);
+
+ if (Concat(nPos))
+ {
+ Search(nStart, nPos);
+ --nPos; // because ++ at end
+ }
+ }
+ }
+ nStart = nEnd + 1;
+ ++nPos;
+ }
+}
+
+bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
+{
+ if ( mvData.empty() )
+ {
+ const ScStyleSheet* pStyle = rDocument.GetDefPattern()->GetStyleSheet();
+ if ( pStyle )
+ {
+ pStyle->SetUsage( ScStyleSheet::Usage::USED );
+ if ( pStyle == &rStyle )
+ return true;
+ }
+ return false;
+ }
+
+ bool bIsUsed = false;
+ SCSIZE nPos = 0;
+
+ while ( nPos < mvData.size() )
+ {
+ const ScStyleSheet* pStyle = mvData[nPos].pPattern->GetStyleSheet();
+ if ( pStyle )
+ {
+ pStyle->SetUsage( ScStyleSheet::Usage::USED );
+ if ( pStyle == &rStyle )
+ {
+ bIsUsed = true;
+ }
+ }
+ nPos++;
+ }
+
+ return bIsUsed;
+}
+
+bool ScAttrArray::IsEmpty() const
+{
+ if ( mvData.empty() )
+ return true;
+
+ if (mvData.size() == 1)
+ {
+ return mvData[0].pPattern == rDocument.GetDefPattern();
+ }
+ else
+ return false;
+}
+
+bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
+{
+ if ( mvData.empty() )
+ return false;
+
+ bool bFound = false;
+ SCSIZE nStart = 0;
+
+ // Skip first entry if more than 1 row.
+ // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr.
+
+ SCSIZE nVisStart = 1;
+ while ( nVisStart < mvData.size() && mvData[nVisStart].pPattern->IsVisibleEqual(*mvData[nVisStart-1].pPattern) )
+ ++nVisStart;
+ if ( nVisStart >= mvData.size() || mvData[nVisStart-1].nEndRow > 0 ) // more than 1 row?
+ nStart = nVisStart;
+
+ while ( nStart < mvData.size() && !bFound )
+ {
+ if ( mvData[nStart].pPattern->IsVisible() )
+ {
+ rFirstRow = nStart ? ( mvData[nStart-1].nEndRow + 1 ) : 0;
+ bFound = true;
+ }
+ else
+ ++nStart;
+ }
+
+ return bFound;
+}
+
+// size (rows) of a range of attributes after cell content where the search is stopped
+// (more than a default page size, 2*42 because it's as good as any number)
+
+const SCROW SC_VISATTR_STOP = 84;
+
+bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const
+{
+ if ( mvData.empty() )
+ {
+ rLastRow = nLastData;
+ return false;
+ }
+
+ // #i30830# changed behavior:
+ // ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows
+ // below the last content cell
+
+ if ( nLastData == rDocument.MaxRow() )
+ {
+ rLastRow = rDocument.MaxRow(); // can't look for attributes below rDocument.MaxRow()
+ return true;
+ }
+
+ // Quick check: last data row in or immediately preceding a run that is the
+ // last attribution down to the end, e.g. default style or column style.
+ SCSIZE nPos = mvData.size() - 1;
+ SCROW nStartRow = (nPos ? mvData[nPos-1].nEndRow + 1 : 0);
+ if (nStartRow <= nLastData + 1)
+ {
+ // Ignore here a few rows if data happens to end within
+ // SC_VISATTR_STOP rows before rDocument.MaxRow().
+ rLastRow = nLastData;
+ return false;
+ }
+
+ // Find a run below last data row.
+ bool bFound = false;
+ Search( nLastData, nPos );
+ while ( nPos < mvData.size() )
+ {
+ // find range of visually equal formats
+ SCSIZE nEndPos = nPos;
+ while ( nEndPos < mvData.size()-1 &&
+ mvData[nEndPos].pPattern->IsVisibleEqual( *mvData[nEndPos+1].pPattern))
+ ++nEndPos;
+ SCROW nAttrStartRow = ( nPos > 0 ) ? ( mvData[nPos-1].nEndRow + 1 ) : 0;
+ if ( nAttrStartRow <= nLastData )
+ nAttrStartRow = nLastData + 1;
+ SCROW nAttrSize = mvData[nEndPos].nEndRow + 1 - nAttrStartRow;
+ if ( nAttrSize >= SC_VISATTR_STOP )
+ break; // while, ignore this range and below
+ else if ( mvData[nEndPos].pPattern->IsVisible() )
+ {
+ rLastRow = mvData[nEndPos].nEndRow;
+ bFound = true;
+ }
+ nPos = nEndPos + 1;
+ }
+
+ return bFound;
+}
+
+bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() )
+ return rDocument.GetDefPattern()->IsVisible();
+
+ SCSIZE nIndex;
+ Search( nStartRow, nIndex );
+ SCROW nThisStart = nStartRow;
+ bool bFound = false;
+ while ( nIndex < mvData.size() && nThisStart <= nEndRow && !bFound )
+ {
+ if ( mvData[nIndex].pPattern->IsVisible() )
+ bFound = true;
+
+ nThisStart = mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+
+ return bFound;
+}
+
+bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther,
+ SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() && rOther.mvData.empty() )
+ {
+ const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
+ const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
+ return ( pDefPattern1 == pDefPattern2 || pDefPattern1->IsVisibleEqual( *pDefPattern2 ) );
+ }
+
+ {
+ const ScAttrArray* pNonDefault = nullptr;
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bDefNonDefCase = false;
+ if ( mvData.empty() && !rOther.mvData.empty() )
+ {
+ pNonDefault = &rOther;
+ pDefPattern = rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+ else if ( !mvData.empty() && rOther.mvData.empty() )
+ {
+ pNonDefault = this;
+ pDefPattern = rOther.rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+
+ if ( bDefNonDefCase )
+ {
+ bool bEqual = true;
+ SCSIZE nPos = 0;
+ if ( nStartRow > 0 )
+ pNonDefault->Search( nStartRow, nPos );
+
+ while ( nPos < pNonDefault->Count() && bEqual )
+ {
+ const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
+ bEqual = ( pNonDefPattern == pDefPattern ||
+ pNonDefPattern->IsVisibleEqual( *pDefPattern ) );
+
+ if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
+ ++nPos;
+ }
+ return bEqual;
+ }
+ }
+
+ bool bEqual = true;
+ SCSIZE nThisPos = 0;
+ SCSIZE nOtherPos = 0;
+ if ( nStartRow > 0 )
+ {
+ Search( nStartRow, nThisPos );
+ rOther.Search( nStartRow, nOtherPos );
+ }
+
+ while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
+ {
+ SCROW nThisRow = mvData[nThisPos].nEndRow;
+ SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
+ const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
+ const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
+ bEqual = ( pThisPattern == pOtherPattern ||
+ pThisPattern->IsVisibleEqual(*pOtherPattern) );
+
+ if ( nThisRow >= nOtherRow )
+ {
+ if ( nOtherRow >= nEndRow ) break;
+ ++nOtherPos;
+ }
+ if ( nThisRow <= nOtherRow )
+ {
+ if ( nThisRow >= nEndRow ) break;
+ ++nThisPos;
+ }
+ }
+
+ return bEqual;
+}
+
+bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const
+{
+ // summarised with IsVisibleEqual
+ if ( mvData.empty() && rOther.mvData.empty() )
+ {
+ const ScPatternAttr* pDefPattern1 = rDocument.GetDefPattern();
+ const ScPatternAttr* pDefPattern2 = rOther.rDocument.GetDefPattern();
+ return ( pDefPattern1 == pDefPattern2 );
+ }
+
+ {
+ const ScAttrArray* pNonDefault = nullptr;
+ const ScPatternAttr* pDefPattern = nullptr;
+ bool bDefNonDefCase = false;
+ if ( mvData.empty() && !rOther.mvData.empty() )
+ {
+ pNonDefault = &rOther;
+ pDefPattern = rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+ else if ( !mvData.empty() && rOther.mvData.empty() )
+ {
+ pNonDefault = this;
+ pDefPattern = rOther.rDocument.GetDefPattern();
+ bDefNonDefCase = true;
+ }
+
+ if ( bDefNonDefCase )
+ {
+ bool bEqual = true;
+ SCSIZE nPos = 0;
+ if ( nStartRow > 0 )
+ pNonDefault->Search( nStartRow, nPos );
+
+ while ( nPos < pNonDefault->Count() && bEqual )
+ {
+ const ScPatternAttr* pNonDefPattern = pNonDefault->mvData[nPos].pPattern;
+ bEqual = ( pNonDefPattern == pDefPattern );
+
+ if ( pNonDefault->mvData[nPos].nEndRow >= nEndRow ) break;
+ ++nPos;
+ }
+ return bEqual;
+ }
+ }
+
+ bool bEqual = true;
+ SCSIZE nThisPos = 0;
+ SCSIZE nOtherPos = 0;
+ if ( nStartRow > 0 )
+ {
+ Search( nStartRow, nThisPos );
+ rOther.Search( nStartRow, nOtherPos );
+ }
+
+ while ( nThisPos<mvData.size() && nOtherPos<rOther.Count() && bEqual )
+ {
+ SCROW nThisRow = mvData[nThisPos].nEndRow;
+ SCROW nOtherRow = rOther.mvData[nOtherPos].nEndRow;
+ const ScPatternAttr* pThisPattern = mvData[nThisPos].pPattern;
+ const ScPatternAttr* pOtherPattern = rOther.mvData[nOtherPos].pPattern;
+ bEqual = ( pThisPattern == pOtherPattern );
+
+ if ( nThisRow >= nOtherRow )
+ {
+ if ( nOtherRow >= nEndRow ) break;
+ ++nOtherPos;
+ }
+ if ( nThisRow <= nOtherRow )
+ {
+ if ( nThisRow >= nEndRow ) break;
+ ++nThisPos;
+ }
+ }
+
+ return bEqual;
+}
+
+bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
+{
+ // Horizontal aggregate are not allowed to be moved out; if whole summary,
+ // here is not recognized
+
+ bool bTest = true;
+ if (!IsEmpty())
+ {
+ SCSIZE nIndex = 0;
+ if ( nStartRow > 0 )
+ Search( nStartRow, nIndex );
+
+ for ( ; nIndex < mvData.size(); nIndex++ )
+ {
+ if ( mvData[nIndex].pPattern->
+ GetItem(ATTR_MERGE_FLAG).IsHorOverlapped() )
+ {
+ bTest = false; // may not be pushed out
+ break;
+ }
+ if ( mvData[nIndex].nEndRow >= nEndRow ) // end of range
+ break;
+ }
+ }
+ return bTest;
+}
+
+bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
+{
+ // if 1st row pushed out is vertically overlapped, summary would be broken
+
+ // rDocument.MaxRow() + 1 - nSize = 1st row pushed out
+
+ if ( mvData.empty() )
+ return !rDocument.GetDefPattern()->
+ GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
+
+ SCSIZE nFirstLost = mvData.size()-1;
+ while ( nFirstLost && mvData[nFirstLost-1].nEndRow >= sal::static_int_cast<SCROW>(rDocument.MaxRow() + 1 - nSize) )
+ --nFirstLost;
+
+ return !mvData[nFirstLost].pPattern->
+ GetItem(ATTR_MERGE_FLAG).IsVerOverlapped();
+}
+
+void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
+{
+ SetDefaultIfNotInit();
+
+ SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor
+ SCSIZE nIndex;
+ Search( nSearch, nIndex );
+
+ // set ScMergeAttr may not be extended (so behind delete again)
+
+ bool bDoMerge = mvData[nIndex].pPattern->GetItem(ATTR_MERGE).IsMerged();
+
+ assert( !bDoMerge || nCol != -1 );
+
+ SCSIZE nRemove = 0;
+ SCSIZE i;
+ for (i = nIndex; i < mvData.size()-1; i++)
+ {
+ SCROW nNew = mvData[i].nEndRow + nSize;
+ if ( nNew >= rDocument.MaxRow() ) // at end?
+ {
+ nNew = rDocument.MaxRow();
+ if (!nRemove)
+ nRemove = i+1; // remove the following?
+ }
+ mvData[i].nEndRow = nNew;
+ }
+
+ // Remove entries at end ?
+
+ if (nRemove && nRemove < mvData.size())
+ DeleteRange( nRemove, mvData.size()-1 );
+
+ if (bDoMerge) // extensively repair (again) ScMergeAttr
+ {
+ // ApplyAttr for areas
+
+ const SfxPoolItem& rDef = rDocument.GetPool()->GetDefaultItem( ATTR_MERGE );
+ for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
+ rDocument.ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
+
+ // reply inserts in this area not summarized
+ }
+
+ // Don't duplicate the merge flags in the inserted row.
+ // #i108488# ScMF::Scenario has to be allowed.
+ RemoveFlags( nStartRow, nStartRow+nSize-1, ScMF::Hor | ScMF::Ver | ScMF::Auto | ScMF::Button );
+}
+
+void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize )
+{
+ SetDefaultIfNotInit();
+ bool bFirst=true;
+ SCSIZE nStartIndex = 0;
+ SCSIZE nEndIndex = 0;
+ SCSIZE i;
+
+ for ( i = 0; i < mvData.size()-1; i++)
+ if (mvData[i].nEndRow >= nStartRow && mvData[i].nEndRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1))
+ {
+ if (bFirst)
+ {
+ nStartIndex = i;
+ bFirst = false;
+ }
+ nEndIndex = i;
+ }
+ if (!bFirst)
+ {
+ SCROW nStart;
+ if (nStartIndex==0)
+ nStart = 0;
+ else
+ nStart = mvData[nStartIndex-1].nEndRow + 1;
+
+ if (nStart < nStartRow)
+ {
+ mvData[nStartIndex].nEndRow = nStartRow - 1;
+ ++nStartIndex;
+ }
+ if (nEndIndex >= nStartIndex)
+ {
+ DeleteRange( nStartIndex, nEndIndex );
+ if (nStartIndex > 0)
+ if ( mvData[nStartIndex-1].pPattern == mvData[nStartIndex].pPattern )
+ DeleteRange( nStartIndex-1, nStartIndex-1 );
+ }
+ }
+ for (i = 0; i < mvData.size()-1; i++)
+ if (mvData[i].nEndRow >= nStartRow)
+ mvData[i].nEndRow -= nSize;
+
+ // Below does not follow the pattern to detect pressure ranges;
+ // instead, only remove merge flags.
+ RemoveFlags( rDocument.MaxRow()-nSize+1, rDocument.MaxRow(), ScMF::Hor | ScMF::Ver | ScMF::Auto );
+}
+
+void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex )
+{
+ SetDefaultIfNotInit();
+ ScDocumentPool* pDocPool = rDocument.GetPool();
+ for (SCSIZE i = nStartIndex; i <= nEndIndex; i++)
+ pDocPool->Remove(*mvData[i].pPattern);
+
+ mvData.erase(mvData.begin() + nStartIndex, mvData.begin() + nEndIndex + 1);
+}
+
+void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow)
+{
+ SetDefaultIfNotInit();
+ if ( nCol != -1 )
+ RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags
+
+ if ( !HasAttrib( nStartRow, nEndRow, HasAttrFlags::Overlapped | HasAttrFlags::AutoFilter) )
+ SetPatternArea( nStartRow, nEndRow, rDocument.GetDefPattern() );
+ else
+ SetPatternAreaSafe( nStartRow, nEndRow, rDocument.GetDefPattern(), true ); // leave merge flags
+}
+
+void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow)
+{
+ SetDefaultIfNotInit();
+ const ScPatternAttr* pDefPattern = rDocument.GetDefPattern();
+
+ SCSIZE nIndex;
+ SCROW nRow;
+ SCROW nThisRow;
+
+ Search( nStartRow, nIndex );
+ nThisRow = (nIndex>0) ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nThisRow < nStartRow) nThisRow = nStartRow;
+
+ while ( nThisRow <= nEndRow )
+ {
+ const ScPatternAttr* pOldPattern = mvData[nIndex].pPattern;
+
+ if ( pOldPattern->GetItemSet().Count() ) // hard attributes ?
+ {
+ nRow = mvData[nIndex].nEndRow;
+ SCROW nAttrRow = std::min( nRow, nEndRow );
+
+ auto pNewPattern = std::make_unique<ScPatternAttr>(*pOldPattern);
+ SfxItemSet& rSet = pNewPattern->GetItemSet();
+ for (sal_uInt16 nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++)
+ if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG)
+ rSet.ClearItem(nId);
+
+ if ( *pNewPattern == *pDefPattern )
+ SetPatternArea( nThisRow, nAttrRow, pDefPattern );
+ else
+ SetPatternArea( nThisRow, nAttrRow, std::move(pNewPattern), true );
+
+ Search( nThisRow, nIndex ); // data changed
+ }
+
+ ++nIndex;
+ nThisRow = mvData[nIndex-1].nEndRow+1;
+ }
+}
+
+/**
+ * Move within a document
+ */
+void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray)
+{
+ SetDefaultIfNotInit();
+ SCROW nStart = nStartRow;
+ for (SCSIZE i = 0; i < mvData.size(); i++)
+ {
+ if ((mvData[i].nEndRow >= nStartRow) && (i == 0 || mvData[i-1].nEndRow < nEndRow))
+ {
+ // copy (bPutToPool=TRUE)
+ rAttrArray.SetPatternArea( nStart, std::min( mvData[i].nEndRow, nEndRow ),
+ mvData[i].pPattern, true );
+ }
+ nStart = std::max( nStart, mvData[i].nEndRow + 1 );
+ }
+ DeleteArea(nStartRow, nEndRow);
+}
+
+/**
+ * Copy between documents (Clipboard)
+ */
+void ScAttrArray::CopyArea(
+ SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const
+{
+ nStartRow -= nDy; // Source
+ nEndRow -= nDy;
+
+ SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
+ SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
+
+ ScDocumentPool* pSourceDocPool = rDocument.GetPool();
+ ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
+ bool bSamePool = (pSourceDocPool==pDestDocPool);
+
+ if ( mvData.empty() )
+ {
+ const ScPatternAttr* pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
+ rAttrArray.SetPatternArea(nDestStart, nDestEnd, pNewPattern);
+ return;
+ }
+
+ for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
+ {
+ if (mvData[i].nEndRow >= nStartRow)
+ {
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ const ScPatternAttr* pNewPattern;
+
+ if (IsDefaultItem( pOldPattern ))
+ {
+ // default: nothing changed
+
+ pNewPattern = &pDestDocPool->GetDefaultItem( ATTR_PATTERN );
+ }
+ else if ( nStripFlags != ScMF::NONE )
+ {
+ ScPatternAttr aTmpPattern( *pOldPattern );
+ ScMF nNewFlags = ScMF::NONE;
+ if ( nStripFlags != ScMF::All )
+ nNewFlags = aTmpPattern.GetItem(ATTR_MERGE_FLAG).GetValue() & ~nStripFlags;
+
+ if ( nNewFlags != ScMF::NONE )
+ aTmpPattern.GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) );
+ else
+ aTmpPattern.GetItemSet().ClearItem( ATTR_MERGE_FLAG );
+
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->Put(aTmpPattern);
+ else
+ pNewPattern = aTmpPattern.PutInPool( &rAttrArray.rDocument, &rDocument );
+ }
+ else
+ {
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->Put(*pOldPattern);
+ else
+ pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
+ }
+
+ rAttrArray.SetPatternArea(nDestStart,
+ std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern);
+ }
+
+ // when pasting from clipboard and skipping filtered rows, the adjusted
+ // end position can be negative
+ nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
+ }
+}
+
+/**
+ * Leave flags
+ * summarized with CopyArea
+ */
+void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, tools::Long nDy, ScAttrArray& rAttrArray )
+{
+ nStartRow -= nDy; // Source
+ nEndRow -= nDy;
+
+ SCROW nDestStart = std::max(static_cast<tools::Long>(static_cast<tools::Long>(nStartRow) + nDy), tools::Long(0));
+ SCROW nDestEnd = std::min(static_cast<tools::Long>(static_cast<tools::Long>(nEndRow) + nDy), tools::Long(rDocument.MaxRow()));
+
+ if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) )
+ {
+ CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray );
+ return;
+ }
+
+ ScDocumentPool* pSourceDocPool = rDocument.GetPool();
+ ScDocumentPool* pDestDocPool = rAttrArray.rDocument.GetPool();
+ bool bSamePool = (pSourceDocPool==pDestDocPool);
+
+ if ( mvData.empty() )
+ {
+ const ScPatternAttr* pNewPattern;
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->Put(*rDocument.GetDefPattern());
+ else
+ pNewPattern = rDocument.GetDefPattern()->PutInPool( &rAttrArray.rDocument, &rDocument );
+
+ rAttrArray.SetPatternAreaSafe(nDestStart, nDestEnd, pNewPattern, false);
+ return;
+ }
+
+
+ for (SCSIZE i = 0; (i < mvData.size()) && (nDestStart <= nDestEnd); i++)
+ {
+ if (mvData[i].nEndRow >= nStartRow)
+ {
+ const ScPatternAttr* pOldPattern = mvData[i].pPattern;
+ const ScPatternAttr* pNewPattern;
+
+ if (bSamePool)
+ pNewPattern = &pDestDocPool->Put(*pOldPattern);
+ else
+ pNewPattern = pOldPattern->PutInPool( &rAttrArray.rDocument, &rDocument );
+
+ rAttrArray.SetPatternAreaSafe(nDestStart,
+ std::min(static_cast<SCROW>(mvData[i].nEndRow + nDy), nDestEnd), pNewPattern, false);
+ }
+
+ // when pasting from clipboard and skipping filtered rows, the adjusted
+ // end position can be negative
+ nDestStart = std::max(static_cast<tools::Long>(nDestStart), static_cast<tools::Long>(mvData[i].nEndRow + nDy + 1));
+ }
+}
+
+SCROW ScAttrArray::SearchStyle(
+ SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ const ScMarkArray* pMarkArray) const
+{
+ bool bFound = false;
+
+ if (pMarkArray)
+ {
+ nRow = pMarkArray->GetNextMarked( nRow, bUp );
+ if (!rDocument.ValidRow(nRow))
+ return nRow;
+ }
+
+ if ( mvData.empty() )
+ {
+ if (rDocument.GetDefPattern()->GetStyleSheet() == pSearchStyle)
+ return nRow;
+
+ nRow = bUp ? -1 : rDocument.MaxRow() + 1;
+ return nRow;
+ }
+
+ SCSIZE nIndex;
+ Search(nRow, nIndex);
+ const ScPatternAttr* pPattern = mvData[nIndex].pPattern;
+
+ while (nIndex < mvData.size() && !bFound)
+ {
+ if (pPattern->GetStyleSheet() == pSearchStyle)
+ {
+ if (pMarkArray)
+ {
+ nRow = pMarkArray->GetNextMarked( nRow, bUp );
+ SCROW nStart = nIndex ? mvData[nIndex-1].nEndRow+1 : 0;
+ if (nRow >= nStart && nRow <= mvData[nIndex].nEndRow)
+ bFound = true;
+ }
+ else
+ bFound = true;
+ }
+
+ if (!bFound)
+ {
+ if (bUp)
+ {
+ if (nIndex==0)
+ {
+ nIndex = mvData.size();
+ nRow = -1;
+ }
+ else
+ {
+ --nIndex;
+ nRow = mvData[nIndex].nEndRow;
+ pPattern = mvData[nIndex].pPattern;
+ }
+ }
+ else
+ {
+ nRow = mvData[nIndex].nEndRow+1;
+ ++nIndex;
+ if (nIndex<mvData.size())
+ pPattern = mvData[nIndex].pPattern;
+ }
+ }
+ }
+
+ OSL_ENSURE( bFound || !rDocument.ValidRow(nRow), "Internal failure in ScAttrArray::SearchStyle" );
+
+ return nRow;
+}
+
+bool ScAttrArray::SearchStyleRange(
+ SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ const ScMarkArray* pMarkArray) const
+{
+ SCROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray );
+ if (rDocument.ValidRow(nStartRow))
+ {
+ if ( mvData.empty() )
+ {
+ rRow = nStartRow;
+ if (bUp)
+ {
+ rEndRow = 0;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
+ if (nMarkEnd>rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+ else
+ {
+ rEndRow = rDocument.MaxRow();
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
+ if (nMarkEnd<rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+
+ return true;
+ }
+
+ SCSIZE nIndex;
+ Search(nStartRow,nIndex);
+
+ rRow = nStartRow;
+ if (bUp)
+ {
+ if (nIndex>0)
+ rEndRow = mvData[nIndex-1].nEndRow + 1;
+ else
+ rEndRow = 0;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, true );
+ if (nMarkEnd>rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+ else
+ {
+ rEndRow = mvData[nIndex].nEndRow;
+ if (pMarkArray)
+ {
+ SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false );
+ if (nMarkEnd<rEndRow)
+ rEndRow = nMarkEnd;
+ }
+ }
+
+ return true;
+ }
+ else
+ return false;
+}
+
+SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow ) const
+{
+ if ( mvData.empty() )
+ return 1;
+
+ SCSIZE nIndex1, nIndex2;
+
+ if( !Search( nStartRow, nIndex1 ) )
+ return 0;
+
+ if( !Search( nEndRow, nIndex2 ) )
+ nIndex2 = mvData.size() - 1;
+
+ return nIndex2 - nIndex1 + 1;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */