diff options
Diffstat (limited to 'sc/source/core/data/attarray.cxx')
-rw-r--r-- | sc/source/core/data/attarray.cxx | 2433 |
1 files changed, 2433 insertions, 0 deletions
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx new file mode 100644 index 000000000000..d0685130155e --- /dev/null +++ b/sc/source/core/data/attarray.cxx @@ -0,0 +1,2433 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sc.hxx" + +//------------------------------------------------------------------------ + +#include "scitems.hxx" +#include <svx/algitem.hxx> +#include <editeng/boxitem.hxx> +#include <editeng/bolnitem.hxx> +#include <editeng/frmdiritem.hxx> +#include <editeng/shaditem.hxx> +#include <editeng/editobj.hxx> +#include <editeng/justifyitem.hxx> +#include <svl/poolcach.hxx> +#include <editeng/fontitem.hxx> +#include <unotools/fontcvt.hxx> + +#include "attarray.hxx" +#include "global.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "markarr.hxx" +#include "rechead.hxx" +#include "globstr.hrc" +#include "segmenttree.hxx" +#include "cell.hxx" + +// STATIC DATA ----------------------------------------------------------- + +//------------------------------------------------------------------------ +using ::editeng::SvxBorderLine; + +ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc ) : + nCol( nNewCol ), + nTab( nNewTab ), + pDocument( pDoc ) +{ + nCount = nLimit = 1; + pData = new ScAttrEntry[1]; + if (pData) + { + pData[0].nRow = MAXROW; + pData[0].pPattern = pDocument->GetDefPattern(); // no put + } +} + +//------------------------------------------------------------------------ + +ScAttrArray::~ScAttrArray() +{ +#if OSL_DEBUG_LEVEL > 1 + TestData(); +#endif + + if (pData) + { + ScDocumentPool* pDocPool = pDocument->GetPool(); + for (SCSIZE i=0; i<nCount; i++) + pDocPool->Remove(*pData[i].pPattern); + + delete[] pData; + } +} + +//------------------------------------------------------------------------ +#if OSL_DEBUG_LEVEL > 1 +void ScAttrArray::TestData() const +{ + + sal_uInt16 nErr = 0; + if (pData) + { + SCSIZE nPos; + for (nPos=0; nPos<nCount; nPos++) + { + if (nPos > 0) + if (pData[nPos].pPattern == pData[nPos-1].pPattern || pData[nPos].nRow <= pData[nPos-1].nRow) + ++nErr; + if (pData[nPos].pPattern->Which() != ATTR_PATTERN) + ++nErr; + } + if ( nPos && pData[nPos-1].nRow != MAXROW ) + ++nErr; + } + if (nErr) + { + ByteString aMsg = ByteString::CreateFromInt32(nErr); + aMsg += " errors in attribute array, column "; + aMsg += ByteString::CreateFromInt32(nCol); + OSL_FAIL( aMsg.GetBuffer() ); + } +} +#endif + +//------------------------------------------------------------------------ + +void ScAttrArray::Reset( const ScPatternAttr* pPattern, sal_Bool bAlloc ) +{ + if (pData) + { + ScDocumentPool* pDocPool = pDocument->GetPool(); + const ScPatternAttr* pOldPattern; + ScAddress aAdrStart( nCol, 0, nTab ); + ScAddress aAdrEnd ( nCol, 0, nTab ); + + for (SCSIZE i=0; i<nCount; i++) + { + // ensure that attributing changes text width of cell + pOldPattern = pData[i].pPattern; + sal_Bool bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + pPattern->GetItemSet(), pOldPattern->GetItemSet() ) ) + { + aAdrStart.SetRow( i ? pData[i-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[i].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); + } + // conditional format or deleted? + if ( &pPattern->GetItem(ATTR_CONDITIONAL) != &pOldPattern->GetItem(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + pOldPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ); + } + pDocPool->Remove(*pOldPattern); + } + delete[] pData; + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, false); + + if (bAlloc) + { + nCount = nLimit = 1; + pData = new ScAttrEntry[1]; + if (pData) + { + ScPatternAttr* pNewPattern = (ScPatternAttr*) &pDocPool->Put(*pPattern); + pData[0].nRow = MAXROW; + pData[0].pPattern = pNewPattern; + } + } + else + { + nCount = nLimit = 0; + pData = NULL; // should be immediately occupied again + } + } +} + + +sal_Bool ScAttrArray::Concat(SCSIZE nPos) +{ + sal_Bool bRet = false; + if (pData && (nPos < nCount)) + { + if (nPos > 0) + { + if (pData[nPos - 1].pPattern == pData[nPos].pPattern) + { + pData[nPos - 1].nRow = pData[nPos].nRow; + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + memmove(&pData[nPos], &pData[nPos + 1], (nCount - nPos - 1) * sizeof(ScAttrEntry)); + pData[nCount - 1].pPattern = NULL; + pData[nCount - 1].nRow = 0; + nCount--; + nPos--; + bRet = sal_True; + } + } + if (nPos + 1 < nCount) + { + if (pData[nPos + 1].pPattern == pData[nPos].pPattern) + { + pData[nPos].nRow = pData[nPos + 1].nRow; + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + memmove(&pData[nPos + 1], &pData[nPos + 2], (nCount - nPos - 2) * sizeof(ScAttrEntry)); + pData[nCount - 1].pPattern = NULL; + pData[nCount - 1].nRow = 0; + nCount--; + bRet = sal_True; + } + } + } + return bRet; +} + +//------------------------------------------------------------------------ + +sal_Bool ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const +{ + long nHi = static_cast<long>(nCount) - 1; + long i = 0; + sal_Bool bFound = (nCount == 1); + if (pData) + { + long nLo = 0; + long nStartRow = 0; + long nEndRow = 0; + while ( !bFound && nLo <= nHi ) + { + i = (nLo + nHi) / 2; + if (i > 0) + nStartRow = (long) pData[i - 1].nRow; + else + nStartRow = -1; + nEndRow = (long) pData[i].nRow; + if (nEndRow < (long) nRow) + nLo = ++i; + else + if (nStartRow >= (long) nRow) + nHi = --i; + else + bFound = sal_True; + } + } + else + bFound = false; + + if (bFound) + nIndex=(SCSIZE)i; + else + nIndex=0; + return bFound; +} + + +const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const +{ + SCSIZE i; + if (Search( nRow, i )) + return pData[i].pPattern; + else + return NULL; +} + + +const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow, + SCROW& rEndRow, SCROW nRow ) const +{ + SCSIZE nIndex; + if ( Search( nRow, nIndex ) ) + { + if ( nIndex > 0 ) + rStartRow = pData[nIndex-1].nRow + 1; + else + rStartRow = 0; + rEndRow = pData[nIndex].nRow; + return pData[nIndex].pPattern; + } + return NULL; +} + +//------------------------------------------------------------------------ + +void ScAttrArray::SetPattern( SCROW nRow, const ScPatternAttr* pPattern, sal_Bool bPutToPool ) +{ + SetPatternArea( nRow, nRow, pPattern, bPutToPool ); +} + +void ScAttrArray::RemoveCellCharAttribs( SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr* pPattern, ScEditDataArray* pDataArray ) +{ + for (SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow) + { + ScBaseCell* pCell; + pDocument->GetCell(nCol, nRow, nTab, pCell); + if (pCell && pCell->GetCellType() == CELLTYPE_EDIT) + { + EditTextObject* pOldData = NULL; + ScEditCell* pEditCell = static_cast<ScEditCell*>(pCell); + if (pDataArray) + pOldData = pEditCell->GetData()->Clone(); + pEditCell->RemoveCharAttribs(*pPattern); + if (pDataArray) + { + EditTextObject* pNewData = pEditCell->GetData()->Clone(); + pDataArray->AddItem(nTab, nCol, nRow, pOldData, pNewData); + } + } + } +} + +void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern, + sal_Bool bPutToPool, ScEditDataArray* pDataArray ) +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + if (bPutToPool) + pPattern = (const ScPatternAttr*) &pDocument->GetPool()->Put(*pPattern); + + if ((nStartRow == 0) && (nEndRow == MAXROW)) + Reset(pPattern); + else + { + SCSIZE nNeeded = nCount + 2; + if ( nLimit < nNeeded ) + { + nLimit += SC_ATTRARRAY_DELTA; + if ( nLimit < nNeeded ) + nLimit = nNeeded; + ScAttrEntry* pNewData = new ScAttrEntry[nLimit]; + memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) ); + delete[] pData; + pData = pNewData; + } + + 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 = pData[ni-1].nRow+1; + } + } + + // ensure that attributing changes text width of cell + // otherwise, conditional formats need to be reset or deleted + while ( ns <= nEndRow ) + { + const SfxItemSet& rNewSet = pPattern->GetItemSet(); + const SfxItemSet& rOldSet = pData[nx].pPattern->GetItemSet(); + + sal_Bool bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( Max(nStartRow,ns) ); + aAdrEnd .SetRow( Min(nEndRow,pData[nx].nRow) ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); + } + if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rOldSet.Get(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rNewSet.Get(ATTR_CONDITIONAL)).GetValue() ); + } + ns = pData[nx].nRow + 1; + nx++; + } + + // continue modifying data array + + SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert) + sal_Bool bCombined = false; + sal_Bool bSplit = false; + if ( nStartRow > 0 ) + { + nInsert = MAXROWCOUNT; + if ( pData[ni].pPattern != pPattern ) + { + if ( ni == 0 || (pData[ni-1].nRow < nStartRow - 1) ) + { // may be a split or a simple insert or just a shrink, + // row adjustment is done further down + if ( pData[ni].nRow > nEndRow ) + bSplit = sal_True; + ni++; + nInsert = ni; + } + else if ( ni > 0 && pData[ni-1].nRow == nStartRow - 1 ) + nInsert = ni; + } + if ( ni > 0 && pData[ni-1].pPattern == pPattern ) + { // combine + pData[ni-1].nRow = nEndRow; + nInsert = MAXROWCOUNT; + bCombined = sal_True; + } + } + else + nInsert = 0; + + SCSIZE nj = ni; // stop position of range to replace + while ( nj < nCount && pData[nj].nRow <= nEndRow ) + nj++; + if ( !bSplit ) + { + if ( nj < nCount && pData[nj].pPattern == pPattern ) + { // combine + if ( ni > 0 ) + { + if ( pData[ni-1].pPattern == pPattern ) + { // adjacent entries + pData[ni-1].nRow = pData[nj].nRow; + nj++; + } + else if ( ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + nInsert = MAXROWCOUNT; + bCombined = sal_True; + } + else if ( ni > 0 && ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + ScDocumentPool* pDocPool = pDocument->GetPool(); + if ( bSplit ) + { // duplicate splitted entry in pool + pDocPool->Put( *pData[ni-1].pPattern ); + } + if ( ni < nj ) + { // remove middle entries + for ( SCSIZE nk=ni; nk<nj; nk++) + { // remove entries from pool + pDocPool->Remove( *pData[nk].pPattern ); + } + if ( !bCombined ) + { // replace one entry + pData[ni].nRow = nEndRow; + pData[ni].pPattern = pPattern; + ni++; + nInsert = MAXROWCOUNT; + } + if ( ni < nj ) + { // remove entries + memmove( pData + ni, pData + nj, (nCount - nj) * sizeof(ScAttrEntry) ); + nCount -= nj - ni; + } + } + + if ( nInsert < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + { // insert or append new entry + if ( nInsert <= nCount ) + { + if ( !bSplit ) + memmove( pData + nInsert + 1, pData + nInsert, + (nCount - nInsert) * sizeof(ScAttrEntry) ); + else + { + memmove( pData + nInsert + 2, pData + nInsert, + (nCount - nInsert) * sizeof(ScAttrEntry) ); + pData[nInsert+1] = pData[nInsert-1]; + nCount++; + } + } + if ( nInsert ) + pData[nInsert-1].nRow = nStartRow - 1; + pData[nInsert].nRow = nEndRow; + pData[nInsert].pPattern = pPattern; + + // Remove character attributes from these cells if the pattern + // is applied during normal session. + if (pDataArray) + RemoveCellCharAttribs(nStartRow, nEndRow, pPattern, pDataArray); + + nCount++; + } + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, false); + } + } + +#if OSL_DEBUG_LEVEL > 1 + TestData(); +#endif +} + + +void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, ScStyleSheet* pStyle ) +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + 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 = pData[nPos].pPattern; + ScPatternAttr* pNewPattern = new ScPatternAttr(*pOldPattern); + pNewPattern->SetStyleSheet(pStyle); + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + nStart = pData[nPos].nRow + 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, sal_True ); + Search( nStart, nPos ); + } + else + { + // 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(); + + sal_Bool bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[nPos].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); + } + + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + if (Concat(nPos)) + Search(nStart, nPos); + else + nPos++; + } + delete pNewPattern; + } + while ((nStart <= nEndRow) && (nPos < nCount)); + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, false); + } + +#if OSL_DEBUG_LEVEL > 1 + TestData(); +#endif +} + + + // const cast, otherwise it will be too inefficient/complicated +#define SET_LINECOLOR(dest,c) \ + if ((dest)) \ + { \ + ((SvxBorderLine*)(dest))->SetColor((c)); \ + } + +#define SET_LINE(dest,src) \ + if ((dest)) \ + { \ + SvxBorderLine* pCast = (SvxBorderLine*)(dest); \ + pCast->SetStyle( (src)->GetStyle( ) ); \ + pCast->SetWidth( (src)->GetWidth( ) ); \ + } + +void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow, + const SvxBorderLine* pLine, sal_Bool bColorOnly ) +{ + if ( bColorOnly && !pLine ) + return; + + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + OSL_FAIL("Search failure"); + return; + } + + do + { + const ScPatternAttr* pOldPattern = pData[nPos].pPattern; + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + const SfxPoolItem* pBoxItem = 0; + SfxItemState eState = rOldSet.GetItemState( ATTR_BORDER, sal_True, &pBoxItem ); + const SfxPoolItem* pTLBRItem = 0; + SfxItemState eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, sal_True, &pTLBRItem ); + const SfxPoolItem* pBLTRItem = 0; + SfxItemState eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, sal_True, &pBLTRItem ); + + if ( (SFX_ITEM_SET == eState) || (SFX_ITEM_SET == eTLBRState) || (SFX_ITEM_SET == eBLTRState) ) + { + ScPatternAttr* pNewPattern = new ScPatternAttr(*pOldPattern); + SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + + SvxBoxItem* pNewBoxItem = pBoxItem ? (SvxBoxItem*)pBoxItem->Clone() : 0; + SvxLineItem* pNewTLBRItem = pTLBRItem ? (SvxLineItem*)pTLBRItem->Clone() : 0; + SvxLineItem* pNewBLTRItem = pBLTRItem ? (SvxLineItem*)pBLTRItem->Clone() : 0; + + // fetch line and update attributes with parameters + + if ( !pLine ) + { + if( pNewBoxItem ) + { + if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( NULL, BOX_LINE_TOP ); + if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( NULL, BOX_LINE_BOTTOM ); + if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( NULL, BOX_LINE_LEFT ); + if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( NULL, BOX_LINE_RIGHT ); + } + if( pNewTLBRItem && pNewTLBRItem->GetLine() ) + pNewTLBRItem->SetLine( 0 ); + if( pNewBLTRItem && pNewBLTRItem->GetLine() ) + pNewBLTRItem->SetLine( 0 ); + } + 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 = pData[nPos].nRow + 1; + + if ( nY1 < nStartRow || nY2 > nEndRow ) + { + if (nY1 < nStartRow) nY1=nStartRow; + if (nY2 > nEndRow) nY2=nEndRow; + SetPatternArea( nY1, nY2, pNewPattern, sal_True ); + Search( nStart, nPos ); + } + else + { + // remove from pool ? + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + + if (Concat(nPos)) + Search(nStart, nPos); + else + nPos++; + } + delete pNewBoxItem; + delete pNewTLBRItem; + delete pNewBLTRItem; + delete pNewPattern; + } + else + { + nStart = pData[nPos].nRow + 1; + nPos++; + } + } + while ((nStart <= nEndRow) && (nPos < nCount)); + } +} + +#undef SET_LINECOLOR +#undef SET_LINE + + +void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache, ScEditDataArray* pDataArray ) +{ +#if OSL_DEBUG_LEVEL > 1 + TestData(); +#endif + + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + 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 = pData[nPos].pPattern; + const ScPatternAttr* pNewPattern = (const ScPatternAttr*) &pCache->ApplyTo( *pOldPattern, sal_True ); + ScDocumentPool::CheckRef( *pOldPattern ); + ScDocumentPool::CheckRef( *pNewPattern ); + if (pNewPattern != pOldPattern) + { + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + nStart = pData[nPos].nRow + 1; + + 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 + { + // ensure attributing changes text-width of cell + + const SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + + sal_Bool bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[nPos].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); + } + + // Reset conditional formats or delete ? + + if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rOldSet.Get(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rNewSet.Get(ATTR_CONDITIONAL)).GetValue() ); + } + + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = pNewPattern; + if (Concat(nPos)) + Search(nStart, nPos); + else + ++nPos; + } + } + else + { + nStart = pData[nPos].nRow + 1; + ++nPos; + } + } + while (nStart <= nEndRow); + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, false); + } + +#if OSL_DEBUG_LEVEL > 1 + TestData(); +#endif +} + +bool ScAttrArray::SetAttrEntries(ScAttrEntry* pNewData, SCSIZE nSize) +{ + if (pData) + { + ScDocumentPool* pDocPool = pDocument->GetPool(); + for (SCSIZE i=0; i<nCount; i++) + pDocPool->Remove(*pData[i].pPattern); + + delete[] pData; + } + + pData = pNewData; + nCount = nLimit = nSize; + return true; +} + +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 == SFX_ITEM_DEFAULT ) + { + SfxItemState eNewState = rSource.GetItemState( nId, sal_True, &pNewItem ); + if ( eNewState == SFX_ITEM_SET ) + { + if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) ) + rMergeSet.InvalidateItem( nId ); + } + } + else if ( eOldState == SFX_ITEM_SET ) // Item gesetzt + { + SfxItemState eNewState = rSource.GetItemState( nId, sal_True, &pNewItem ); + if ( eNewState == SFX_ITEM_SET ) + { + if ( pNewItem != pOldItem ) // beide gepuhlt + 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, sal_Bool bDeep ) const +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + OSL_FAIL("Search failure"); + return; + } + + do + { + // similar patterns must not be repeated + + const ScPatternAttr* pPattern = pData[nPos].pPattern; + if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 ) + { + const SfxItemSet& rThisSet = pPattern->GetItemSet(); + if (rState.pItemSet) + { + if (bDeep) + lcl_MergeDeep( *rState.pItemSet, rThisSet ); + else + rState.pItemSet->MergeValues( rThisSet, false ); + } + else + { + // first pattern - copied from parent + rState.pItemSet = new SfxItemSet( *rThisSet.GetPool(), rThisSet.GetRanges() ); + rState.pItemSet->Set( rThisSet, bDeep ); + } + + rState.pOld2 = rState.pOld1; + rState.pOld1 = pPattern; + } + + nStart = pData[nPos].nRow + 1; + ++nPos; + } + while (nStart <= nEndRow); + } +} + + + +// assemble border + +sal_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 = NULL; + return true; // another line -> don't care +} + + +void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, + ScLineFlags& rFlags, const ScPatternAttr* pPattern, + sal_Bool bLeft, SCCOL nDistRight, sal_Bool bTop, SCROW nDistBottom ) +{ + // right/bottom border set when connected together + const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE); + if ( rMerge.GetColMerge() == nDistRight + 1 ) + nDistRight = 0; + if ( rMerge.GetRowMerge() == nDistBottom + 1 ) + nDistBottom = 0; + + const SvxBoxItem* pCellFrame = (SvxBoxItem*) &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, BOX_LINE_TOP ); + } + else + { + if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_HORI ); + } + + if (nDistBottom == 0) + { + if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_BOTTOM ); + } + else + { + if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_HORI ); + } + + if (bLeft) + { + if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_LEFT ); + } + else + { + if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_VERT ); + } + + if (nDistRight == 0) + { + if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_RIGHT ); + } + else + { + if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_VERT ); + } +} + + +void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, + ScLineFlags& rFlags, + SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight ) const +{ + const ScPatternAttr* pPattern; + + if (nStartRow == nEndRow) + { + pPattern = GetPattern( nStartRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_True, 0 ); + } + else + { + pPattern = GetPattern( nStartRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_True, + nEndRow-nStartRow ); + + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nStartRow+1, nStartIndex ); + Search( nEndRow-1, nEndIndex ); + for (SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pPattern = (ScPatternAttr*) pData[i].pPattern; + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, + nEndRow - Min( pData[i].nRow, (SCROW)(nEndRow-1) ) ); + // nDistBottom here always > 0 + } + + pPattern = GetPattern( nEndRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, false, 0 ); + } +} + +// +// apply border +// + +// ApplyFrame - on an entry into the array + +sal_Bool ScAttrArray::ApplyFrame( const SvxBoxItem* pBoxItem, + const SvxBoxInfoItem* pBoxInfoItem, + SCROW nStartRow, SCROW nEndRow, + sal_Bool bLeft, SCCOL nDistRight, sal_Bool bTop, SCROW nDistBottom ) +{ + DBG_ASSERT( pBoxItem && pBoxInfoItem, "Linienattribute fehlen!" ); + + const ScPatternAttr* pPattern = GetPattern( nStartRow ); + const SvxBoxItem* pOldFrame = (const SvxBoxItem*) + &pPattern->GetItemSet().Get( ATTR_BORDER ); + + // right/bottom border set when connected together + const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE); + if ( rMerge.GetColMerge() == nDistRight + 1 ) + nDistRight = 0; + if ( rMerge.GetRowMerge() == nDistBottom + 1 ) + nDistBottom = 0; + + SvxBoxItem aNewFrame( *pOldFrame ); + + if ( bLeft ? pBoxInfoItem->IsValid(VALID_LEFT) : pBoxInfoItem->IsValid(VALID_VERT) ) + aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), + BOX_LINE_LEFT ); + if ( (nDistRight==0) ? pBoxInfoItem->IsValid(VALID_RIGHT) : pBoxInfoItem->IsValid(VALID_VERT) ) + aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), + BOX_LINE_RIGHT ); + if ( bTop ? pBoxInfoItem->IsValid(VALID_TOP) : pBoxInfoItem->IsValid(VALID_HORI) ) + aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), + BOX_LINE_TOP ); + if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(VALID_BOTTOM) : pBoxInfoItem->IsValid(VALID_HORI) ) + aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), + BOX_LINE_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* pLineOuter, const SvxBoxInfoItem* pLineInner, + SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight ) +{ + if (nStartRow == nEndRow) + ApplyFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, sal_True, 0 ); + else + { + ApplyFrame( pLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight, + sal_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 = Min( (SCROW)(nEndRow-1), (SCROW)(pData[i].nRow) ); + sal_Bool bChanged = ApplyFrame( pLineOuter, pLineInner, nTmpStart, nTmpEnd, + bLeft, nDistRight, false, nEndRow-nTmpEnd ); + nTmpStart = nTmpEnd+1; + if (bChanged) + { + Search(nTmpStart, i); + Search(nEndRow-1, nEndIndex); + } + else + i++; + } + } + + ApplyFrame( pLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, false, 0 ); + } +} + + +long lcl_LineSize( const SvxBorderLine& rLine ) +{ + // only one line -> half width, min. 20 + // double line -> half line spacing + (per min. 20) + + long nTotal = 0; + sal_uInt16 nWidth = Max( rLine.GetOutWidth(), rLine.GetInWidth() ); + sal_uInt16 nDist = rLine.GetDistance(); + if (nDist) + { + DBG_ASSERT( rLine.GetOutWidth() && rLine.GetInWidth(), + "Line has a distance, but only a width?" ); + + nTotal += ( nDist > 20 ) ? nDist : 20; + nTotal += ( nWidth > 20 ) ? nWidth : 20; + } + else if (nWidth) + nTotal += ( nWidth > 20 ) ? nWidth : 20; + + // also halved ? + + return nTotal; +} + +sal_Bool ScAttrArray::HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes, + sal_Bool bLeft, sal_Bool bRight ) const +{ + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nRow1, nStartIndex ); + Search( nRow2, nEndIndex ); + sal_Bool bFound = false; + + const SvxBoxItem* pItem = 0; + const SvxBorderLine* pLine = 0; + long nCmp; + + // top + + pItem = (const SvxBoxItem*) &pData[nStartIndex].pPattern->GetItem(ATTR_BORDER); + pLine = pItem->GetTop(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Top() ) + rSizes.Top() = nCmp; + bFound = sal_True; + } + + // down + + if ( nEndIndex != nStartIndex ) + pItem = (const SvxBoxItem*) &pData[nEndIndex].pPattern->GetItem(ATTR_BORDER); + pLine = pItem->GetBottom(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Bottom() ) + rSizes.Bottom() = nCmp; + bFound = sal_True; + } + + if ( bLeft || bRight ) + for ( SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pItem = (const SvxBoxItem*) &pData[i].pPattern->GetItem(ATTR_BORDER); + + if (bLeft) + { + pLine = pItem->GetLeft(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Left() ) + rSizes.Left() = nCmp; + bFound = sal_True; + } + } + + if (bRight) + { + pLine = pItem->GetRight(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Right() ) + rSizes.Right() = nCmp; + bFound = sal_True; + } + } + } + + return bFound; +} + +// Test if field contains specific attribute + +bool ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const +{ + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nRow1, nStartIndex ); + Search( nRow2, nEndIndex ); + bool bFound = false; + + for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++) + { + const ScPatternAttr* pPattern = pData[i].pPattern; + if ( nMask & HASATTR_MERGED ) + { + const ScMergeAttr* pMerge = + (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 ) + bFound = true; + } + if ( nMask & ( HASATTR_OVERLAPPED | HASATTR_NOTOVERLAPPED | HASATTR_AUTOFILTER ) ) + { + const ScMergeFlagAttr* pMergeFlag = + (const ScMergeFlagAttr*) &pPattern->GetItem( ATTR_MERGE_FLAG ); + if ( (nMask & HASATTR_OVERLAPPED) && pMergeFlag->IsOverlapped() ) + bFound = true; + if ( (nMask & HASATTR_NOTOVERLAPPED) && !pMergeFlag->IsOverlapped() ) + bFound = true; + if ( (nMask & HASATTR_AUTOFILTER) && pMergeFlag->HasAutoFilter() ) + bFound = true; + } + if ( nMask & HASATTR_LINES ) + { + const SvxBoxItem* pBox = + (const SvxBoxItem*) &pPattern->GetItem( ATTR_BORDER ); + if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() ) + bFound = true; + } + if ( nMask & HASATTR_SHADOW ) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + if ( pShadow->GetLocation() != SVX_SHADOW_NONE ) + bFound = true; + } + if ( nMask & HASATTR_CONDITIONAL ) + { + const SfxUInt32Item* pConditional = + (const SfxUInt32Item*) &pPattern->GetItem( ATTR_CONDITIONAL ); + if ( pConditional->GetValue() != 0 ) + bFound = true; + } + if ( nMask & HASATTR_PROTECTED ) + { + const ScProtectionAttr* pProtect = + (const ScProtectionAttr*) &pPattern->GetItem( ATTR_PROTECTION ); + if ( pProtect->GetProtection() || pProtect->GetHideCell() ) + bFound = true; + } + if ( nMask & HASATTR_ROTATE ) + { + const SfxInt32Item* pRotate = + (const SfxInt32Item*) &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 & HASATTR_NEEDHEIGHT ) + { + if (pPattern->GetCellOrientation() != SVX_ORIENTATION_STANDARD) + bFound = true; + else if (((const SfxBoolItem&)pPattern->GetItem( ATTR_LINEBREAK )).GetValue()) + bFound = true; + else if ((SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern-> + GetItem( ATTR_HOR_JUSTIFY )).GetValue() == SVX_HOR_JUSTIFY_BLOCK) + bFound = true; + else if (((const SfxUInt32Item&)pPattern->GetItem( ATTR_CONDITIONAL )).GetValue()) + bFound = true; + else if (((const SfxInt32Item&)pPattern->GetItem( ATTR_ROTATE_VALUE )).GetValue()) + bFound = true; + } + if ( nMask & ( HASATTR_SHADOW_RIGHT | HASATTR_SHADOW_DOWN ) ) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + SvxShadowLocation eLoc = pShadow->GetLocation(); + if ( nMask & HASATTR_SHADOW_RIGHT ) + if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + bFound = true; + if ( nMask & HASATTR_SHADOW_DOWN ) + if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + bFound = true; + } + if ( nMask & HASATTR_RTL ) + { + const SvxFrameDirectionItem& rDirection = + (const SvxFrameDirectionItem&) pPattern->GetItem( ATTR_WRITINGDIR ); + if ( rDirection.GetValue() == FRMDIR_HORI_RIGHT_TOP ) + bFound = true; + } + if ( nMask & HASATTR_RIGHTORCENTER ) + { + // called only if the sheet is LTR, so physical=logical alignment can be assumed + SvxCellHorJustify eHorJust = (SvxCellHorJustify) + ((const SvxHorJustifyItem&) pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue(); + if ( eHorJust == SVX_HOR_JUSTIFY_RIGHT || eHorJust == SVX_HOR_JUSTIFY_CENTER ) + bFound = true; + } + } + + return bFound; +} + +// Area around any given summaries expand and adapt any MergeFlag (bRefresh) +sal_Bool ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow, + SCCOL& rPaintCol, SCROW& rPaintRow, + sal_Bool bRefresh, sal_Bool bAttrs ) +{ + const ScPatternAttr* pPattern; + const ScMergeAttr* pItem; + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nStartRow, nStartIndex ); + Search( nEndRow, nEndIndex ); + sal_Bool bFound = false; + + for (SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pPattern = pData[i].pPattern; + pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + SCsCOL nCountX = pItem->GetColMerge(); + SCsROW nCountY = pItem->GetRowMerge(); + if (nCountX>1 || nCountY>1) + { + SCROW nThisRow = (i>0) ? pData[i-1].nRow+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 = sal_True; + + if (bAttrs) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + SvxShadowLocation eLoc = pShadow->GetLocation(); + if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + if ( nMergeEndCol+1 > rPaintCol && nMergeEndCol < MAXCOL ) + rPaintCol = nMergeEndCol+1; + if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + if ( nMergeEndRow+1 > rPaintRow && nMergeEndRow < MAXROW ) + rPaintRow = nMergeEndRow+1; + } + + if (bRefresh) + { + if ( nMergeEndCol > nThisCol ) + pDocument->ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, pData[i].nRow, + nTab, SC_MF_HOR ); + if ( nMergeEndRow > nThisRow ) + pDocument->ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow, + nTab, SC_MF_VER ); + if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow ) + pDocument->ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow, + nTab, SC_MF_HOR | SC_MF_VER ); + + Search( nThisRow, i ); // Data changed + Search( nStartRow, nStartIndex ); + Search( nEndRow, nEndIndex ); + } + } + } + + return bFound; +} + + +sal_Bool ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow) +{ + sal_Bool bFound = false; + const ScPatternAttr* pPattern; + const ScMergeAttr* pItem; + SCSIZE nIndex; + + Search( nStartRow, nIndex ); + SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisStart < nStartRow) + nThisStart = nStartRow; + + while ( nThisStart <= nEndRow ) + { + SCROW nThisEnd = pData[nIndex].nRow; + if (nThisEnd > nEndRow) + nThisEnd = nEndRow; + + pPattern = pData[nIndex].pPattern; + pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + SCsCOL nCountX = pItem->GetColMerge(); + SCsROW nCountY = pItem->GetRowMerge(); + if (nCountX>1 || nCountY>1) + { + const ScMergeAttr* pAttr = (const ScMergeAttr*) + &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); + const ScMergeFlagAttr* pFlagAttr = (const ScMergeFlagAttr*) + &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE_FLAG ); + + DBG_ASSERT( 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 ); + + ScPatternAttr* pNewPattern = new ScPatternAttr( pDocument->GetPool() ); + SfxItemSet* pSet = &pNewPattern->GetItemSet(); + pSet->Put( *pFlagAttr ); + pDocument->ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow, + nTab, *pNewPattern ); + delete pNewPattern; + + Search( nThisEnd, nIndex ); // data changed + } + + ++nIndex; + if ( nIndex < nCount ) + nThisStart = pData[nIndex-1].nRow+1; + else + nThisStart = MAXROW+1; // End + } + + return bFound; +} + +// Remove field, but leave MergeFlags + +void ScAttrArray::DeleteAreaSafe(SCROW nStartRow, SCROW nEndRow) +{ + SetPatternAreaSafe( nStartRow, nEndRow, pDocument->GetDefPattern(), sal_True ); +} + + +void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr* pWantedPattern, sal_Bool bDefault ) +{ + const ScPatternAttr* pOldPattern; + const ScMergeFlagAttr* pItem; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + sal_Bool bFirstUse = sal_True; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + if (pOldPattern != pWantedPattern) //! else-Zweig ? + { + if (nThisRow < nStartRow) nThisRow = nStartRow; + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + pItem = (const ScMergeFlagAttr*) &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. + ScPatternAttr* pNewPattern = new ScPatternAttr( *pWantedPattern ); + SfxItemSet* pSet = &pNewPattern->GetItemSet(); + pSet->Put( *pItem ); + SetPatternArea( nThisRow, nAttrRow, pNewPattern, sal_True ); + delete pNewPattern; + } + 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 = pData[nIndex-1].nRow+1; + } +} + + +sal_Bool ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags ) +{ + const ScPatternAttr* pOldPattern; + + sal_Int16 nOldValue; + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + sal_Bool bChanged = false; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue(); + if ( (nOldValue | nFlags) != nOldValue ) + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true ); + Search( nThisRow, nIndex ); // data changed + bChanged = true; + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } + + return bChanged; +} + + +sal_Bool ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags ) +{ + const ScPatternAttr* pOldPattern; + + sal_Int16 nOldValue; + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + sal_Bool bChanged = false; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue(); + if ( (nOldValue & ~nFlags) != nOldValue ) + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true ); + Search( nThisRow, nIndex ); // data changed + bChanged = true; + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } + + return bChanged; +} + + +void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich ) +{ + const ScPatternAttr* pOldPattern; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + if ( pOldPattern->HasItemsSet( pWhich ) ) + { + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.ClearItems( pWhich ); + + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, true ); + Search( nThisRow, nIndex ); // data changed + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } +} + + +void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, sal_Bool bIncrement ) +{ + SCSIZE nIndex; + Search( nStartRow, nIndex ); + SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisStart < nStartRow) nThisStart = nStartRow; + + while ( nThisStart <= nEndRow ) + { + const ScPatternAttr* pOldPattern = pData[nIndex].pPattern; + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + const SfxPoolItem* pItem; + + sal_Bool bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, false, &pItem ) != SFX_ITEM_SET + || ((const SvxHorJustifyItem*)pItem)->GetValue() != SVX_HOR_JUSTIFY_LEFT ); + sal_uInt16 nOldValue = ((const SfxUInt16Item&)rOldSet.Get( ATTR_INDENT )).GetValue(); + sal_uInt16 nNewValue = nOldValue; + if ( bIncrement ) + { + if ( nNewValue < SC_MAX_INDENT ) + { + nNewValue += SC_INDENT_STEP; + if ( nNewValue > SC_MAX_INDENT ) nNewValue = SC_MAX_INDENT; + } + } + else + { + if ( nNewValue > 0 ) + { + if ( nNewValue > SC_INDENT_STEP ) + nNewValue -= SC_INDENT_STEP; + else + nNewValue = 0; + } + } + + if ( bNeedJust || nNewValue != nOldValue ) + { + SCROW nThisEnd = pData[nIndex].nRow; + SCROW nAttrRow = Min( nThisEnd, nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( SfxUInt16Item( ATTR_INDENT, nNewValue ) ); + if ( bNeedJust ) + aNewPattern.GetItemSet().Put( + SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) ); + SetPatternArea( nThisStart, nAttrRow, &aNewPattern, sal_True ); + + nThisStart = nThisEnd + 1; + Search( nThisStart, nIndex ); // data changed + } + else + { + nThisStart = pData[nIndex].nRow + 1; + ++nIndex; + } + } +} + + +SCsROW ScAttrArray::GetNextUnprotected( SCsROW nRow, sal_Bool bUp ) const +{ + long nRet = nRow; + if (VALIDROW(nRow)) + { + SCSIZE nIndex; + Search(nRow, nIndex); + while (((const ScProtectionAttr&)pData[nIndex].pPattern-> + GetItem(ATTR_PROTECTION)).GetProtection()) + { + if (bUp) + { + if (nIndex==0) + return -1; // not found + --nIndex; + nRet = pData[nIndex].nRow; + } + else + { + nRet = pData[nIndex].nRow+1; + ++nIndex; + if (nIndex>=nCount) + return MAXROW+1; // not found + } + } + } + return nRet; +} + +void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset ) +{ + SCROW nStart = 0; + SCSIZE nPos = 0; + while (nPos < nCount) + { + SCROW nEnd = pData[nPos].nRow; + if (pData[nPos].pPattern->GetStyleSheet() == pStyleSheet) + { + rUsedRows.setTrue(nStart, nEnd); + + if (bReset) + { + ScPatternAttr* pNewPattern = new ScPatternAttr(*pData[nPos].pPattern); + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pNewPattern->SetStyleSheet( (ScStyleSheet*) + pDocument->GetStyleSheetPool()-> + Find( ScGlobal::GetRscString(STR_STYLENAME_STANDARD), + SFX_STYLE_FAMILY_PARA, + SFXSTYLEBIT_AUTO | SCSTYLEBIT_STANDARD ) ); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + delete pNewPattern; + + if (Concat(nPos)) + { + Search(nStart, nPos); + --nPos; // because ++ at end + } + } + } + nStart = nEnd + 1; + ++nPos; + } +} + + +sal_Bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle, + sal_Bool bGatherAllStyles ) const +{ + sal_Bool bIsUsed = false; + SCSIZE nPos = 0; + + while ( nPos < nCount ) + { + const ScStyleSheet* pStyle = pData[nPos].pPattern->GetStyleSheet(); + if ( pStyle ) + { + pStyle->SetUsage( ScStyleSheet::USED ); + if ( pStyle == &rStyle ) + { + if ( !bGatherAllStyles ) + return sal_True; + bIsUsed = sal_True; + } + } + nPos++; + } + + return bIsUsed; +} + + +sal_Bool ScAttrArray::IsEmpty() const +{ + if (nCount == 1) + { + if ( pData[0].pPattern != pDocument->GetDefPattern() ) + return false; + else + return sal_True; + } + else + return false; +} + + +sal_Bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const +{ + DBG_ASSERT( nCount, "nCount == 0" ); + + sal_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 < nCount && pData[nVisStart].pPattern->IsVisibleEqual(*pData[nVisStart-1].pPattern) ) + ++nVisStart; + if ( nVisStart >= nCount || pData[nVisStart-1].nRow > 0 ) // more than 1 row? + nStart = nVisStart; + + while ( nStart < nCount && !bFound ) + { + if ( pData[nStart].pPattern->IsVisible() ) + { + rFirstRow = nStart ? ( pData[nStart-1].nRow + 1 ) : 0; + bFound = sal_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; + +sal_Bool ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const +{ + // #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 sal_True; + } + + sal_Bool bFound = false; + + // loop backwards from the end instead of using Search, assuming that + // there usually aren't many attributes below the last cell + + SCSIZE nPos = nCount; + while ( nPos > 0 && pData[nPos-1].nRow > nLastData ) + { + SCSIZE nEndPos = nPos - 1; + SCSIZE nStartPos = nEndPos; // find range of visually equal formats + while ( nStartPos > 0 && + pData[nStartPos-1].nRow > nLastData && + pData[nStartPos-1].pPattern->IsVisibleEqual(*pData[nStartPos].pPattern) ) + --nStartPos; + + SCROW nAttrStartRow = ( nStartPos > 0 ) ? ( pData[nStartPos-1].nRow + 1 ) : 0; + if ( nAttrStartRow <= nLastData ) + nAttrStartRow = nLastData + 1; + SCROW nAttrSize = pData[nEndPos].nRow + 1 - nAttrStartRow; + if ( nAttrSize >= SC_VISATTR_STOP ) + { + bFound = false; // ignore this range and below + } + else if ( !bFound && pData[nEndPos].pPattern->IsVisible() ) + { + rLastRow = pData[nEndPos].nRow; + bFound = sal_True; + } + + nPos = nStartPos; // look further from the top of the range + } + + return bFound; +} + + +sal_Bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const +{ + SCSIZE nIndex; + Search( nStartRow, nIndex ); + SCROW nThisStart = nStartRow; + sal_Bool bFound = false; + while ( nIndex < nCount && nThisStart <= nEndRow && !bFound ) + { + if ( pData[nIndex].pPattern->IsVisible() ) + bFound = sal_True; + + nThisStart = pData[nIndex].nRow + 1; + ++nIndex; + } + + return bFound; +} + + +sal_Bool ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther, + SCROW nStartRow, SCROW nEndRow ) const +{ + sal_Bool bEqual = sal_True; + SCSIZE nThisPos = 0; + SCSIZE nOtherPos = 0; + if ( nStartRow > 0 ) + { + Search( nStartRow, nThisPos ); + rOther.Search( nStartRow, nOtherPos ); + } + + while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual ) + { + SCROW nThisRow = pData[nThisPos].nRow; + SCROW nOtherRow = rOther.pData[nOtherPos].nRow; + const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern; + const ScPatternAttr* pOtherPattern = rOther.pData[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; +} + + +sal_Bool ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const +{ + // summarised with IsVisibleEqual + + sal_Bool bEqual = sal_True; + SCSIZE nThisPos = 0; + SCSIZE nOtherPos = 0; + if ( nStartRow > 0 ) + { + Search( nStartRow, nThisPos ); + rOther.Search( nStartRow, nOtherPos ); + } + + while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual ) + { + SCROW nThisRow = pData[nThisPos].nRow; + SCROW nOtherRow = rOther.pData[nOtherPos].nRow; + const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern; + const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern; + bEqual = ( pThisPattern == pOtherPattern ); + + if ( nThisRow >= nOtherRow ) + { + if ( nOtherRow >= nEndRow ) break; + ++nOtherPos; + } + if ( nThisRow <= nOtherRow ) + { + if ( nThisRow >= nEndRow ) break; + ++nThisPos; + } + } + + return bEqual; +} + + +sal_Bool ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const +{ + // Horizontal aggregate are not allowed to be moved out; if whole summary, + // here is not recognized + + sal_Bool bTest = sal_True; + if (!IsEmpty()) + { + SCSIZE nIndex = 0; + if ( nStartRow > 0 ) + Search( nStartRow, nIndex ); + + for ( ; nIndex < nCount; nIndex++ ) + { + if ( ((const ScMergeFlagAttr&)pData[nIndex].pPattern-> + GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped() ) + { + bTest = false; // may not be pushed out + break; + } + if ( pData[nIndex].nRow >= nEndRow ) // end of range + break; + } + } + return bTest; +} + + +sal_Bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const +{ + // if 1st row pushed out is vertically overlapped, summary would be broken + + if (pData) + { + // MAXROW + 1 - nSize = 1st row pushed out + + SCSIZE nFirstLost = nCount-1; + while ( nFirstLost && pData[nFirstLost-1].nRow >= sal::static_int_cast<SCROW>(MAXROW + 1 - nSize) ) + --nFirstLost; + + if ( ((const ScMergeFlagAttr&)pData[nFirstLost].pPattern-> + GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped() ) + return false; + } + + return sal_True; +} + + +void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize ) +{ + if (!pData) + return; + + SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // expand predecessor + SCSIZE nIndex; + Search( nSearch, nIndex ); + + // set ScMergeAttr may not be extended (so behind delete again) + + sal_Bool bDoMerge = ((const ScMergeAttr&) pData[nIndex].pPattern->GetItem(ATTR_MERGE)).IsMerged(); + + SCSIZE nRemove = 0; + SCSIZE i; + for (i = nIndex; i < nCount-1; i++) + { + SCROW nNew = pData[i].nRow + nSize; + if ( nNew >= MAXROW ) // at end? + { + nNew = MAXROW; + if (!nRemove) + nRemove = i+1; // remove the following? + } + pData[i].nRow = nNew; + } + + // Remove entries at end ? + + if (nRemove && nRemove < nCount) + DeleteRange( nRemove, nCount-1 ); + + if (bDoMerge) // extensively repair (again) ScMergeAttr + { + // ApplyAttr for areas + + const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); + for (SCSIZE nAdd=0; nAdd<nSize; nAdd++) + pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef ); + + // reply inserts in this area not summarized + } + + // Don't duplicate the merge flags in the inserted row. + // #i108488# SC_MF_SCENARIO has to be allowed. + RemoveFlags( nStartRow, nStartRow+nSize-1, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO | SC_MF_BUTTON ); +} + + +void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize ) +{ + if (pData) + { + sal_Bool bFirst=sal_True; + SCSIZE nStartIndex = 0; + SCSIZE nEndIndex = 0; + SCSIZE i; + + for ( i = 0; i < nCount-1; i++) + if (pData[i].nRow >= nStartRow && pData[i].nRow <= 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 = pData[nStartIndex-1].nRow + 1; + + if (nStart < nStartRow) + { + pData[nStartIndex].nRow = nStartRow - 1; + ++nStartIndex; + } + if (nEndIndex >= nStartIndex) + { + DeleteRange( nStartIndex, nEndIndex ); + if (nStartIndex > 0) + if ( pData[nStartIndex-1].pPattern == pData[nStartIndex].pPattern ) + DeleteRange( nStartIndex-1, nStartIndex-1 ); + } + } + for (i = 0; i < nCount-1; i++) + if (pData[i].nRow >= nStartRow) + pData[i].nRow -= nSize; + + // Below does not follow the pattern to detect pressure ranges; + // instead, only remove merge flags. + RemoveFlags( MAXROW-nSize+1, MAXROW, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO ); + } +} + + +void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex ) +{ + ScDocumentPool* pDocPool = pDocument->GetPool(); + for (SCSIZE i = nStartIndex; i <= nEndIndex; i++) + pDocPool->Remove(*pData[i].pPattern); + + memmove( &pData[nStartIndex], &pData[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ScAttrEntry) ); + nCount -= nEndIndex-nStartIndex+1; +} + + +void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow) +{ + RemoveAreaMerge( nStartRow, nEndRow ); // remove from combined flags + + if ( !HasAttrib( nStartRow, nEndRow, HASATTR_OVERLAPPED | HASATTR_AUTOFILTER) ) + SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() ); + else + DeleteAreaSafe( nStartRow, nEndRow ); // leave merge flags +} + + +void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow) +{ + const ScPatternAttr* pDefPattern = pDocument->GetDefPattern(); + const ScPatternAttr* pOldPattern; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + + if ( pOldPattern->GetItemSet().Count() ) // hard attributes ? + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)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, false ); + else + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True ); + + Search( nThisRow, nIndex ); // data changed + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } +} + + +// move within a document + +void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray) +{ + SCROW nStart = nStartRow; + for (SCSIZE i = 0; i < nCount; i++) + { + if ((pData[i].nRow >= nStartRow) && ((i==0) ? sal_True : pData[i-1].nRow < nEndRow)) + { + // copy (bPutToPool=TRUE) + rAttrArray.SetPatternArea( nStart, Min( (SCROW)pData[i].nRow, (SCROW)nEndRow ), + pData[i].pPattern, sal_True ); + } + nStart = Max( (SCROW)nStart, (SCROW)(pData[i].nRow + 1) ); + } + DeleteArea(nStartRow, nEndRow); +} + + +// copy between documents (Clipboard) + +void ScAttrArray::CopyArea( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray, + sal_Int16 nStripFlags ) +{ + nStartRow -= nDy; // Source + nEndRow -= nDy; + + SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0); + SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW); + + ScDocumentPool* pSourceDocPool = pDocument->GetPool(); + ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool(); + sal_Bool bSamePool = (pSourceDocPool==pDestDocPool); + + for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++) + { + if (pData[i].nRow >= nStartRow) + { + const ScPatternAttr* pOldPattern = pData[i].pPattern; + const ScPatternAttr* pNewPattern; + + if (IsDefaultItem( pOldPattern )) + { + // default: nothing changed + + pNewPattern = (const ScPatternAttr*) + &pDestDocPool->GetDefaultItem( ATTR_PATTERN ); + } + else if ( nStripFlags ) + { + ScPatternAttr* pTmpPattern = new ScPatternAttr( *pOldPattern ); + sal_Int16 nNewFlags = 0; + if ( nStripFlags != SC_MF_ALL ) + nNewFlags = ((const ScMergeFlagAttr&)pTmpPattern->GetItem(ATTR_MERGE_FLAG)). + GetValue() & ~nStripFlags; + + if ( nNewFlags ) + pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) ); + else + pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG ); + + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pTmpPattern); + else + pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument ); + delete pTmpPattern; + } + else + { + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern); + else + pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); + } + + rAttrArray.SetPatternArea(nDestStart, + Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern); + } + + // when pasting from clipboard and skipping filtered rows, the adjusted + // end position can be negative + nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + 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 = Max((long)((long)nStartRow + nDy), (long) 0); + SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW); + + if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HASATTR_OVERLAPPED ) ) + { + CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray ); + return; + } + + ScDocumentPool* pSourceDocPool = pDocument->GetPool(); + ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool(); + sal_Bool bSamePool = (pSourceDocPool==pDestDocPool); + + for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++) + { + if (pData[i].nRow >= nStartRow) + { + const ScPatternAttr* pOldPattern = pData[i].pPattern; + const ScPatternAttr* pNewPattern; + + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern); + else + pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); + + rAttrArray.SetPatternAreaSafe(nDestStart, + Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern, false); + } + + // when pasting from clipboard and skipping filtered rows, the adjusted + // end position can be negative + nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1)); + } +} + + +SCsROW ScAttrArray::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle, + sal_Bool bUp, ScMarkArray* pMarkArray ) +{ + sal_Bool bFound = false; + + if (pMarkArray) + { + nRow = pMarkArray->GetNextMarked( nRow, bUp ); + if (!VALIDROW(nRow)) + return nRow; + } + + SCSIZE nIndex; + Search(nRow, nIndex); + const ScPatternAttr* pPattern = pData[nIndex].pPattern; + + while (nIndex < nCount && !bFound) + { + if (pPattern->GetStyleSheet() == pSearchStyle) + { + if (pMarkArray) + { + nRow = pMarkArray->GetNextMarked( nRow, bUp ); + SCROW nStart = nIndex ? pData[nIndex-1].nRow+1 : 0; + if (nRow >= nStart && nRow <= pData[nIndex].nRow) + bFound = sal_True; + } + else + bFound = sal_True; + } + + if (!bFound) + { + if (bUp) + { + if (nIndex==0) + { + nIndex = nCount; + nRow = -1; + } + else + { + --nIndex; + nRow = pData[nIndex].nRow; + pPattern = pData[nIndex].pPattern; + } + } + else + { + nRow = pData[nIndex].nRow+1; + ++nIndex; + if (nIndex<nCount) + pPattern = pData[nIndex].pPattern; + } + } + } + + DBG_ASSERT( bFound || !ValidRow(nRow), "Internal failure in in ScAttrArray::SearchStyle" ); + + return nRow; +} + + +sal_Bool ScAttrArray::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow, + const ScStyleSheet* pSearchStyle, sal_Bool bUp, ScMarkArray* pMarkArray ) +{ + SCsROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray ); + if (VALIDROW(nStartRow)) + { + SCSIZE nIndex; + Search(nStartRow,nIndex); + + rRow = nStartRow; + if (bUp) + { + if (nIndex>0) + rEndRow = pData[nIndex-1].nRow + 1; + else + rEndRow = 0; + if (pMarkArray) + { + SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, sal_True ); + if (nMarkEnd>rEndRow) + rEndRow = nMarkEnd; + } + } + else + { + rEndRow = pData[nIndex].nRow; + if (pMarkArray) + { + SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, false ); + if (nMarkEnd<rEndRow) + rEndRow = nMarkEnd; + } + } + + return sal_True; + } + else + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |