/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using ::editeng::SvxBorderLine; ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc, ScAttrArray* pDefaultColAttrArray ) : nCol( nNewCol ), nTab( nNewTab ), pDocument( pDoc ) { if ( nCol != -1 && pDefaultColAttrArray && !pDefaultColAttrArray->mvData.empty()) { 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 = static_cast( &pDocument->GetPool()->Put( aNewPattern ) ); bool bNumFormatChanged = false; if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, mvData[nIdx].pPattern->GetItemSet(), pDocument->GetDefPattern()->GetItemSet() ) ) { aAdrStart.SetRow( nIdx ? mvData[nIdx-1].nEndRow+1 : 0 ); aAdrEnd.SetRow( mvData[nIdx].nEndRow ); pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); } } } } ScAttrArray::~ScAttrArray() { #if DEBUG_SC_TESTATTRARRAY TestData(); #endif ScDocumentPool* pDocPool = pDocument->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 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 != 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( SC_ATTRARRAY_DELTA, nNeeded ); mvData.reserve( nNewLimit ); mvData.emplace_back(); mvData[0].nEndRow = MAXROW; mvData[0].pPattern = pDocument->GetDefPattern(); // no put } void ScAttrArray::Reset( const ScPatternAttr* pPattern ) { ScDocumentPool* pDocPool = pDocument->GetPool(); ScAddress aAdrStart( nCol, 0, nTab ); ScAddress aAdrEnd ( nCol, 0, nTab ); for (SCSIZE i=0; iGetItemSet(), pOldPattern->GetItemSet() ) ) { aAdrStart.SetRow( i ? mvData[i-1].nEndRow+1 : 0 ); aAdrEnd .SetRow( mvData[i].nEndRow ); pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); } } pDocPool->Remove(*pOldPattern); } mvData.resize(0); pDocument->SetStreamValid(nTab, false); mvData.resize(1); const ScPatternAttr* pNewPattern = static_cast( &pDocPool->Put(*pPattern) ); mvData[0].nEndRow = 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; pDocument->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; pDocument->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. */ 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(); */ long nHi = static_cast(mvData.size()) - 1; long i = 0; bool bFound = (mvData.size() == 1); long nLo = 0; long nStartRow = 0; while ( !bFound && nLo <= nHi ) { i = (nLo + nHi) / 2; if (i > 0) nStartRow = static_cast(mvData[i - 1].nEndRow); else nStartRow = -1; const long nEndRow = static_cast(mvData[i].nEndRow); if (nEndRow < static_cast(nRow)) nLo = ++i; else if (nStartRow >= static_cast(nRow)) nHi = --i; else bFound = true; } if (bFound) nIndex=static_cast(i); else nIndex=0; return bFound; } const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const { if ( mvData.empty() ) { if ( !ValidRow( nRow ) ) return nullptr; return pDocument->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 ( !ValidRow( nRow ) ) return nullptr; rStartRow = 0; rEndRow = MAXROW; return pDocument->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(!ValidRow(nStartRow) || !ValidRow(nEndRow)) return; if(nEndRow < nStartRow) return; SCROW nTempStartRow = nStartRow; SCROW nTempEndRow = nEndRow; do { const ScPatternAttr* pPattern = GetPattern(nTempStartRow); std::unique_ptr pNewPattern; if(pPattern) { pNewPattern.reset( new ScPatternAttr(*pPattern) ); SCROW nPatternStartRow; SCROW nPatternEndRow; GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow ); nTempEndRow = std::min( nPatternEndRow, nEndRow ); const SfxPoolItem* pItem = nullptr; pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem ); std::vector< sal_uInt32 > aCondFormatData; if(pItem) aCondFormatData = static_cast(pItem)->GetCondFormatData(); aCondFormatData.push_back(nIndex); ScCondFormatItem aItem; aItem.SetCondFormatData( aCondFormatData ); pNewPattern->GetItemSet().Put( aItem ); } else { pNewPattern.reset( new ScPatternAttr( pDocument->GetPool() ) ); ScCondFormatItem aItem; aItem.AddCondFormatData(nIndex); pNewPattern->GetItemSet().Put( aItem ); nTempEndRow = nEndRow; } SetPatternArea( nTempStartRow, nTempEndRow, pNewPattern.get(), true ); nTempStartRow = nTempEndRow + 1; } while(nTempEndRow < nEndRow); } void ScAttrArray::RemoveCondFormat( SCROW nStartRow, SCROW nEndRow, sal_uInt32 nIndex ) { if(!ValidRow(nStartRow) || !ValidRow(nEndRow)) return; if(nEndRow < nStartRow) return; SCROW nTempStartRow = nStartRow; SCROW nTempEndRow = nEndRow; do { const ScPatternAttr* pPattern = GetPattern(nTempStartRow); if(pPattern) { ScPatternAttr aPattern( *pPattern ); SCROW nPatternStartRow; SCROW nPatternEndRow; GetPatternRange( nPatternStartRow, nPatternEndRow, nTempStartRow ); nTempEndRow = std::min( nPatternEndRow, nEndRow ); const SfxPoolItem* pItem = nullptr; pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, true, &pItem ); if(pItem) { std::vector< sal_uInt32 > aCondFormatData = static_cast(pItem)->GetCondFormatData(); std::vector::iterator itr = std::find(aCondFormatData.begin(), aCondFormatData.end(), nIndex); if(itr != aCondFormatData.end() || nIndex == 0) { ScCondFormatItem aItem; if (nIndex == 0) aCondFormatData.clear(); else aCondFormatData.erase(itr); aItem.SetCondFormatData( aCondFormatData ); aPattern.GetItemSet().Put( aItem ); SetPatternArea( nTempStartRow, nTempEndRow, &aPattern, true ); } } } else { return; } nTempStartRow = nTempEndRow + 1; } while(nTempEndRow < nEndRow); } void ScAttrArray::SetPattern( SCROW nRow, const ScPatternAttr* pPattern, bool bPutToPool ) { SetPatternArea( nRow, nRow, pPattern, bPutToPool ); } void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr* pPattern, ScEditDataArray* pDataArray ) { assert( nCol != -1 ); for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow) { ScAddress aPos(nCol, nRow, nTab); ScRefCellValue aCell(*pDocument, aPos); if (aCell.meType != CELLTYPE_EDIT || !aCell.mpEditText) continue; std::unique_ptr 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(*aCell.mpEditText), *pPattern); if (pDataArray) { std::unique_ptr 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 = MAXROW; mvData[0].pPattern = pDocument->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; } void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern, bool bPutToPool, ScEditDataArray* pDataArray ) { if (ValidRow(nStartRow) && ValidRow(nEndRow)) { if (bPutToPool) pPattern = static_cast(&pDocument->GetPool()->Put(*pPattern)); if ((nStartRow == 0) && (nEndRow == MAXROW)) Reset(pPattern); else { SCSIZE nNeeded = mvData.size() + 2; SetDefaultIfNotInit( nNeeded ); if ( mvData.capacity() < nNeeded ) { size_t nLimit = mvData.capacity() + SC_ATTRARRAY_DELTA; if ( nLimit < nNeeded ) nLimit = nNeeded; mvData.reserve(nLimit); } 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 while ( ns <= nEndRow ) { if ( nCol != -1 ) { 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) ); pDocument->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 = MAXROWCOUNT; 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 ( ni > 0 && mvData[ni-1].nEndRow == nStartRow - 1 ) nInsert = ni; } if ( ni > 0 && mvData[ni-1].pPattern == pPattern ) { // combine mvData[ni-1].nEndRow = nEndRow; nInsert = MAXROWCOUNT; 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 = MAXROWCOUNT; bCombined = true; } else if ( ni > 0 && ni == nInsert ) mvData[ni-1].nEndRow = nStartRow - 1; // shrink } ScDocumentPool* pDocPool = pDocument->GetPool(); if ( bSplit ) { // duplicate splitted entry in pool pDocPool->Put( *mvData[ni-1].pPattern ); } if ( ni < nj ) { // remove middle entries for ( SCSIZE nk=ni; nkRemove( *mvData[nk].pPattern ); } if ( !bCombined ) { // replace one entry mvData[ni].nEndRow = nEndRow; mvData[ni].pPattern = pPattern; ni++; nInsert = MAXROWCOUNT; } if ( ni < nj ) { // remove entries mvData.erase( mvData.begin() + ni, mvData.begin() + nj); } } if ( nInsert < sal::static_int_cast(MAXROWCOUNT) ) { // 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); } pDocument->SetStreamValid(nTab, false); } } #if DEBUG_SC_TESTATTRARRAY TestData(); #endif } void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle ) { if (ValidRow(nStartRow) && ValidRow(nEndRow)) { 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 pNewPattern(new ScPatternAttr(*pOldPattern)); pNewPattern->SetStyleSheet(const_cast(&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, pNewPattern.get(), 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 ); pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); } } pDocument->GetPool()->Remove(*mvData[nPos].pPattern); mvData[nPos].pPattern = static_cast( &pDocument->GetPool()->Put(*pNewPattern)); if (Concat(nPos)) Search(nStart, nPos); else nPos++; } } while ((nStart <= nEndRow) && (nPos < mvData.size())); pDocument->SetStreamValid(nTab, false); } #if DEBUG_SC_TESTATTRARRAY TestData(); #endif } // const cast, otherwise it will be too inefficient/complicated #define SET_LINECOLOR(dest,c) \ if ((dest)) \ { \ const_cast(dest)->SetColor((c)); \ } #define SET_LINE(dest,src) \ if ((dest)) \ { \ SvxBorderLine* pCast = const_cast(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 (ValidRow(nStartRow) && ValidRow(nEndRow)) { 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 SfxPoolItem* pBoxItem = nullptr; SfxItemState eState = rOldSet.GetItemState( ATTR_BORDER, true, &pBoxItem ); const SfxPoolItem* pTLBRItem = nullptr; SfxItemState eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem ); const SfxPoolItem* pBLTRItem = nullptr; SfxItemState eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem ); if ( (SfxItemState::SET == eState) || (SfxItemState::SET == eTLBRState) || (SfxItemState::SET == eBLTRState) ) { std::unique_ptr pNewPattern(new ScPatternAttr(*pOldPattern)); SfxItemSet& rNewSet = pNewPattern->GetItemSet(); SCROW nY1 = nStart; SCROW nY2 = mvData[nPos].nEndRow; SvxBoxItem* pNewBoxItem = pBoxItem ? static_cast(pBoxItem->Clone()) : nullptr; SvxLineItem* pNewTLBRItem = pTLBRItem ? static_cast(pTLBRItem->Clone()) : nullptr; SvxLineItem* pNewBLTRItem = pBLTRItem ? static_cast(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 ) { SET_LINECOLOR( pNewBoxItem->GetTop(), aColor ); SET_LINECOLOR( pNewBoxItem->GetBottom(), aColor ); SET_LINECOLOR( pNewBoxItem->GetLeft(), aColor ); SET_LINECOLOR( pNewBoxItem->GetRight(), aColor ); } if( pNewTLBRItem ) SET_LINECOLOR( pNewTLBRItem->GetLine(), aColor ); if( pNewBLTRItem ) SET_LINECOLOR( pNewBLTRItem->GetLine(), aColor ); } else { if( pNewBoxItem ) { SET_LINE( pNewBoxItem->GetTop(), pLine ); SET_LINE( pNewBoxItem->GetBottom(), pLine ); SET_LINE( pNewBoxItem->GetLeft(), pLine ); SET_LINE( pNewBoxItem->GetRight(), pLine ); } if( pNewTLBRItem ) SET_LINE( pNewTLBRItem->GetLine(), pLine ); if( pNewBLTRItem ) SET_LINE( pNewBLTRItem->GetLine(), pLine ); } } if( pNewBoxItem ) rNewSet.Put( *pNewBoxItem ); if( pNewTLBRItem ) rNewSet.Put( *pNewTLBRItem ); if( pNewBLTRItem ) rNewSet.Put( *pNewBLTRItem ); nStart = mvData[nPos].nEndRow + 1; if ( nY1 < nStartRow || nY2 > nEndRow ) { if (nY1 < nStartRow) nY1=nStartRow; if (nY2 > nEndRow) nY2=nEndRow; SetPatternArea( nY1, nY2, pNewPattern.get(), true ); Search( nStart, nPos ); } else { // remove from pool ? pDocument->GetPool()->Remove(*mvData[nPos].pPattern); mvData[nPos].pPattern = static_cast( &pDocument->GetPool()->Put(*pNewPattern) ); if (Concat(nPos)) Search(nStart, nPos); else nPos++; } delete pNewBoxItem; delete pNewTLBRItem; delete pNewBLTRItem; } else { nStart = mvData[nPos].nEndRow + 1; nPos++; } } while ((nStart <= nEndRow) && (nPos < mvData.size())); } } #undef SET_LINECOLOR #undef SET_LINE void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache, ScEditDataArray* pDataArray, bool* const pIsChanged ) { #if DEBUG_SC_TESTATTRARRAY TestData(); #endif if (ValidRow(nStartRow) && ValidRow(nEndRow)) { 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( &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 ); pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); } } pDocument->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); pDocument->SetStreamValid(nTab, false); } #if DEBUG_SC_TESTATTRARRAY TestData(); #endif } void ScAttrArray::SetAttrEntries(std::vector && vNewData) { ScDocumentPool* pDocPool = pDocument->GetPool(); for (auto const & rEntry : mvData) pDocPool->Remove(*rEntry.pPattern); mvData = std::move(vNewData); } 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 (ValidRow(nStartRow) && ValidRow(nEndRow)) { 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 = pDocument->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 = o3tl::make_unique( *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 = 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(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, pDocument->GetDefPattern(), bLeft, nDistRight, true, 0 ); } } // apply border // ApplyFrame - on an entry into the array bool ScAttrArray::ApplyFrame( const SvxBoxItem* pBoxItem, const SvxBoxInfoItem* pBoxInfoItem, SCROW nStartRow, SCROW nEndRow, bool bLeft, SCCOL nDistRight, bool bTop, SCROW nDistBottom ) { OSL_ENSURE( pBoxItem && 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=pDocument->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( pBoxItem->GetLeft(), SvxBoxItemLine::RIGHT ); if ( pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) ) aNewFrame.SetLine( pBoxItem->GetRight(), SvxBoxItemLine::LEFT ); } else { if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) ) aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT ); if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) ) aNewFrame.SetLine( bLeft ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT ); } } else { if ( bLeft ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::LEFT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) ) aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), SvxBoxItemLine::LEFT ); if ( (nDistRight==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::RIGHT) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::VERT) ) aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), SvxBoxItemLine::RIGHT ); } if ( bTop ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::TOP) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) ) aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), SvxBoxItemLine::TOP ); if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::BOTTOM) : pBoxInfoItem->IsValid(SvxBoxInfoItemValidFlags::HORI) ) aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), SvxBoxItemLine::BOTTOM ); if (aNewFrame == *pOldFrame) { // nothing to do return false; } else { SfxItemPoolCache aCache( pDocument->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) { if (nStartRow == nEndRow) ApplyFrame(&rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 0); else if ( !mvData.empty() ) { 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(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); } else { ApplyFrame(&rLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, true, 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 ) { bool bContainsCondFormat = pPattern->GetItem( ATTR_CONDITIONAL ).GetCondFormatData().empty(); if ( bContainsCondFormat ) 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 ) // pDocument->GetCondResult() is valid only for real columns. { SCROW nRowStartCond = std::max( nRow1, i ? mvData[i-1].nEndRow + 1: 0 ); SCROW nRowEndCond = std::min( nRow2, mvData[i].nEndRow ); bool bFoundCond = false; for(SCROW nRowCond = nRowStartCond; nRowCond <= nRowEndCond && !bFoundCond; ++nRowCond) { const SfxItemSet* pSet = pDocument->GetCondResult( nCol, nRowCond, nTab ); const SfxPoolItem* pItem; if( pSet && pSet->GetItemState( ATTR_PROTECTION, true, &pItem ) == SfxItemState::SET ) { const ScProtectionAttr* pCondProtect = static_cast(pItem); 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 SfxInt32Item* pRotate = &pPattern->GetItem( ATTR_ROTATE_VALUE ); // 90 or 270 degrees is former SvxOrientationItem - only look for other values // (see ScPatternAttr::GetCellOrientation) sal_Int32 nAngle = pRotate->GetValue(); if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 ) 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(pDocument->GetDefPattern(), nMask, 0, 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::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 pDocument->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 <= MAXCOL) rPaintCol = nMergeEndCol; if (nMergeEndRow > rPaintRow && nMergeEndRow <= MAXROW) rPaintRow = nMergeEndRow; bFound = true; if (bRefresh) { if ( nMergeEndCol > nThisCol ) pDocument->ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, mvData[i].nEndRow, nTab, ScMF::Hor ); if ( nMergeEndRow > nThisRow ) pDocument->ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow, nTab, ScMF::Ver ); if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow ) pDocument->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 = &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); const ScMergeFlagAttr* pFlagAttr = &pDocument->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++) pDocument->ApplyAttr( nThisCol, nThisRow, nTab, *pAttr ); std::unique_ptr pNewPattern(new ScPatternAttr( pDocument->GetPool() )); SfxItemSet* pSet = &pNewPattern->GetItemSet(); pSet->Put( *pFlagAttr ); pDocument->ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow, nTab, *pNewPattern ); pNewPattern.reset(); Search( nThisEnd, nIndex ); // data changed } ++nIndex; if ( nIndex < mvData.size() ) nThisStart = mvData[nIndex-1].nEndRow+1; else nThisStart = 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 pNewPattern(new ScPatternAttr( *pWantedPattern )); SfxItemSet* pSet = &pNewPattern->GetItemSet(); pSet->Put( *pItem ); SetPatternArea( nThisRow, nAttrRow, pNewPattern.get(), true ); } else { if ( !bDefault ) { if (bFirstUse) bFirstUse = false; else // it's in the pool pDocument->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 ); ScPatternAttr aNewPattern(*pOldPattern); aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) ); SetPatternArea( nThisRow, nAttrRow, &aNewPattern, 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 ); ScPatternAttr aNewPattern(*pOldPattern); aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) ); SetPatternArea( nThisRow, nAttrRow, &aNewPattern, 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 ) ) { ScPatternAttr aNewPattern(*pOldPattern); aNewPattern.ClearItems( pWhich ); nRow = mvData[nIndex].nEndRow; SCROW nAttrRow = std::min( nRow, nEndRow ); SetPatternArea( nThisRow, nAttrRow, &aNewPattern, 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 SfxPoolItem* pItem; bool bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem ) != SfxItemState::SET || (static_cast(pItem)->GetValue() != SvxCellHorJustify::Left && static_cast(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 long nColWidth = static_cast(pDocument->GetColWidth(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 ); ScPatternAttr aNewPattern(*pOldPattern); aNewPattern.GetItemSet().Put( SfxUInt16Item( ATTR_INDENT, nNewValue ) ); if ( bNeedJust ) aNewPattern.GetItemSet().Put( SvxHorJustifyItem( SvxCellHorJustify::Left, ATTR_HOR_JUSTIFY ) ); SetPatternArea( nThisStart, nAttrRow, &aNewPattern, true ); nThisStart = nThisEnd + 1; Search( nThisStart, nIndex ); // data changed } else { nThisStart = mvData[nIndex].nEndRow + 1; ++nIndex; } } } SCROW ScAttrArray::GetNextUnprotected( SCROW nRow, bool bUp ) const { long nRet = nRow; if (ValidRow(nRow)) { if ( mvData.empty() ) { if ( bUp ) return -1; else return 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 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) { std::unique_ptr pNewPattern(new ScPatternAttr(*mvData[nPos].pPattern)); pDocument->GetPool()->Remove(*mvData[nPos].pPattern); pNewPattern->SetStyleSheet( static_cast( pDocument->GetStyleSheetPool()-> Find( ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Para, SfxStyleSearchBits::Auto | SfxStyleSearchBits::ScStandard ) ) ); mvData[nPos].pPattern = static_cast( &pDocument->GetPool()->Put(*pNewPattern)); pNewPattern.reset(); 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 = pDocument->GetDefPattern()->GetStyleSheet(); if ( pStyle ) { pStyle->SetUsage( ScStyleSheet::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::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 == pDocument->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 == MAXROW ) { rLastRow = MAXROW; // can't look for attributes below 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 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 pDocument->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 = pDocument->GetDefPattern(); const ScPatternAttr* pDefPattern2 = rOther.pDocument->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 = pDocument->GetDefPattern(); bDefNonDefCase = true; } else if ( !mvData.empty() && rOther.mvData.empty() ) { pNonDefault = this; pDefPattern = rOther.pDocument->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 ( nThisPosIsVisibleEqual(*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 = pDocument->GetDefPattern(); const ScPatternAttr* pDefPattern2 = rOther.pDocument->GetDefPattern(); return ( pDefPattern1 == pDefPattern2 ); } { const ScAttrArray* pNonDefault = nullptr; const ScPatternAttr* pDefPattern = nullptr; bool bDefNonDefCase = false; if ( mvData.empty() && !rOther.mvData.empty() ) { pNonDefault = &rOther; pDefPattern = pDocument->GetDefPattern(); bDefNonDefCase = true; } else if ( !mvData.empty() && rOther.mvData.empty() ) { pNonDefault = this; pDefPattern = rOther.pDocument->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= 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 // MAXROW + 1 - nSize = 1st row pushed out if ( mvData.empty() ) return !pDocument->GetDefPattern()-> GetItem(ATTR_MERGE_FLAG).IsVerOverlapped(); SCSIZE nFirstLost = mvData.size()-1; while ( nFirstLost && mvData[nFirstLost-1].nEndRow >= sal::static_int_cast(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 >= MAXROW ) // at end? { nNew = 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 = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); for (SCSIZE nAdd=0; nAddApplyAttr( 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(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( MAXROW-nSize+1, MAXROW, ScMF::Hor | ScMF::Ver | ScMF::Auto ); } void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex ) { SetDefaultIfNotInit(); ScDocumentPool* pDocPool = pDocument->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, pDocument->GetDefPattern() ); else SetPatternAreaSafe( nStartRow, nEndRow, pDocument->GetDefPattern(), true ); // leave merge flags } void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow) { SetDefaultIfNotInit(); const ScPatternAttr* pDefPattern = pDocument->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 ); ScPatternAttr aNewPattern(*pOldPattern); SfxItemSet& rSet = aNewPattern.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 ( aNewPattern == *pDefPattern ) SetPatternArea( nThisRow, nAttrRow, pDefPattern ); else SetPatternArea( nThisRow, nAttrRow, &aNewPattern, 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, long nDy, ScAttrArray& rAttrArray, ScMF nStripFlags) const { nStartRow -= nDy; // Source nEndRow -= nDy; SCROW nDestStart = std::max(static_cast(static_cast(nStartRow) + nDy), long(0)); SCROW nDestEnd = std::min(static_cast(static_cast(nEndRow) + nDy), long(MAXROW)); ScDocumentPool* pSourceDocPool = pDocument->GetPool(); ScDocumentPool* pDestDocPool = rAttrArray.pDocument->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 ) { std::unique_ptr pTmpPattern(new ScPatternAttr( *pOldPattern )); ScMF nNewFlags = ScMF::NONE; if ( nStripFlags != ScMF::All ) nNewFlags = pTmpPattern->GetItem(ATTR_MERGE_FLAG).GetValue() & ~nStripFlags; if ( nNewFlags != ScMF::NONE ) pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) ); else pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG ); if (bSamePool) pNewPattern = static_cast( &pDestDocPool->Put(*pTmpPattern) ); else pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument ); } else { if (bSamePool) pNewPattern = static_cast( &pDestDocPool->Put(*pOldPattern) ); else pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); } rAttrArray.SetPatternArea(nDestStart, std::min(static_cast(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(nDestStart), static_cast(mvData[i].nEndRow + nDy + 1)); } } /** * Leave flags * summarized with CopyArea */ void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray ) { nStartRow -= nDy; // Source nEndRow -= nDy; SCROW nDestStart = std::max(static_cast(static_cast(nStartRow) + nDy), long(0)); SCROW nDestEnd = std::min(static_cast(static_cast(nEndRow) + nDy), long(MAXROW)); if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HasAttrFlags::Overlapped ) ) { CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray ); return; } ScDocumentPool* pSourceDocPool = pDocument->GetPool(); ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool(); bool bSamePool = (pSourceDocPool==pDestDocPool); if ( mvData.empty() ) { const ScPatternAttr* pNewPattern; if (bSamePool) pNewPattern = static_cast( &pDestDocPool->Put(*pDocument->GetDefPattern())); else pNewPattern = pDocument->GetDefPattern()->PutInPool( rAttrArray.pDocument, pDocument ); 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 = static_cast( &pDestDocPool->Put(*pOldPattern) ); else pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); rAttrArray.SetPatternAreaSafe(nDestStart, std::min(static_cast(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(nDestStart), static_cast(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 (!ValidRow(nRow)) return nRow; } if ( mvData.empty() ) { if (pDocument->GetDefPattern()->GetStyleSheet() == pSearchStyle) return nRow; nRow = bUp ? -1 : 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 (nIndexGetMarkEnd( nStartRow, true ); if (nMarkEnd>rEndRow) rEndRow = nMarkEnd; } } else { rEndRow = MAXROW; if (pMarkArray) { SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false ); if (nMarkEnd0) 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