blob: a2abc6f5f6489e89eda778d91b0091ecb934bcac [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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 <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"
#undef DBG_INVALIDATE
#define DBGOUTPUT(s) \
DBG_ERROR( String("Invalidate ") + String(s) + String(": ") \
+ String(nCol) + String('/') + String(aAdrStart.Row()) + String('/') + String(nTab) \
+ String(" bis ") \
+ String(nCol) + String('/') + String(aAdrEnd.Row()) + String('/') + String(nTab) \
);
// STATIC DATA -----------------------------------------------------------
//------------------------------------------------------------------------
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(); // ohne Put !!!
}
}
//------------------------------------------------------------------------
ScAttrArray::~ScAttrArray()
{
#ifdef DBG_UTIL
TestData();
#endif
if (pData)
{
ScDocumentPool* pDocPool = pDocument->GetPool();
for (SCSIZE i=0; i<nCount; i++)
pDocPool->Remove(*pData[i].pPattern);
delete[] pData;
}
}
//------------------------------------------------------------------------
#ifdef DBG_UTIL
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);
DBG_ERROR( 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++)
{
// ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
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 );
#ifdef DBG_INVALIDATE
DBGOUTPUT("Reset");
#endif
}
// bedingtes Format gesetzt oder geloescht?
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, sal_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; // muss sofort wieder belegt werden !
}
}
}
sal_Bool ScAttrArray::Concat(SCSIZE nPos)
{
sal_Bool bRet = sal_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 nLo = 0;
long nHi = static_cast<long>(nCount) - 1;
long nStartRow = 0;
long nEndRow = 0;
long i = 0;
sal_Bool bFound = (nCount == 1);
if (pData)
{
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 = sal_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 );
}
bool ScAttrArray::Reserve( SCSIZE nReserve )
{
if ( nCount <= nReserve )
{
if( ScAttrEntry* pNewData = new (std::nothrow) ScAttrEntry[nReserve] )
{
nLimit = nReserve;
memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) );
delete[] pData;
pData = pNewData;
return true;
}
else
return false;
}
else
return false;
}
void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern, sal_Bool bPutToPool )
{
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;
}
}
// ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
// oder bedingte Formate neu gesetzt oder geloescht werden
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 );
#ifdef DBG_INVALIDATE
DBGOUTPUT("SetPatternArea");
#endif
}
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 = sal_False;
sal_Bool bSplit = sal_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;
nCount++;
}
if (pDocument->IsStreamValid(nTab))
pDocument->SetStreamValid(nTab, sal_False);
}
}
// InfoBox(0, String(nCount) + String(" Eintraege") ).Execute();
#ifdef DBG_UTIL
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 ))
{
DBG_ERROR("Search-Fehler");
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
{
// ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
// bedingte Formate in Vorlagen gibt es (noch) nicht
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 );
#ifdef DBG_INVALIDATE
DBGOUTPUT("ApplyStyleArea");
#endif
}
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, sal_False);
}
#ifdef DBG_UTIL
TestData();
#endif
}
// const wird weggecastet, weil es sonst
// zu ineffizient/kompliziert wird!
#define SET_LINECOLOR(dest,c) \
if ((dest)) \
{ \
((SvxBorderLine*)(dest))->SetColor((c)); \
}
#define SET_LINE(dest,src) \
if ((dest)) \
{ \
SvxBorderLine* pCast = (SvxBorderLine*)(dest); \
pCast->SetOutWidth((src)->GetOutWidth()); \
pCast->SetInWidth ((src)->GetInWidth()); \
pCast->SetDistance((src)->GetDistance()); \
}
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 ))
{
DBG_ERROR("Search-Fehler");
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;
// Linienattribute holen und mit Parametern aktualisieren
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
{
//! aus Pool loeschen?
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 )
{
#ifdef DBG_UTIL
TestData();
#endif
if (ValidRow(nStartRow) && ValidRow(nEndRow))
{
SCSIZE nPos;
SCROW nStart=0;
if (!Search( nStartRow, nPos ))
{
DBG_ERROR("Search-Fehler");
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 );
Search( nStart, nPos );
}
else
{
// ueberpruefen, ob Attributierung die Textbreite der Zelle aendert
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 );
#ifdef DBG_INVALIDATE
DBGOUTPUT("ApplyCacheArea");
#endif
}
// bedingte Formate neu gesetzt oder geloescht ?
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
{
//!!!!!!!!!!!!!!!!!! mit diesem Remove gibt es Abstuerze (Calc1 Import)
//! pDocument->GetPool()->Remove(*pNewPattern);
nStart = pData[nPos].nRow + 1;
++nPos;
}
}
while (nStart <= nEndRow);
if (pDocument->IsStreamValid(nTab))
pDocument->SetStreamValid(nTab, sal_False);
}
#ifdef DBG_UTIL
TestData();
#endif
}
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 hat keinen Parent
SfxItemState eOldState = rMergeSet.GetItemState( nId, sal_False, &pOldItem );
if ( eOldState == SFX_ITEM_DEFAULT ) // 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 bleibt 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 ))
{
DBG_ERROR("Search-Fehler");
return;
}
do
{
// gleiche Patterns muessen nicht mehrfach angesehen werden
const ScPatternAttr* pPattern = pData[nPos].pPattern;
if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 )
{
const SfxItemSet& rThisSet = pPattern->GetItemSet();
if (rState.pItemSet)
{
// (*ppSet)->MergeValues( rThisSet, sal_False );
// geht nicht, weil die Vorlagen nicht beruecksichtigt werden
if (bDeep)
lcl_MergeDeep( *rState.pItemSet, rThisSet );
else
rState.pItemSet->MergeValues( rThisSet, sal_False );
}
else
{
// erstes Pattern - in Set ohne Parent kopieren
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);
}
}
// Umrandung zusammenbauen
sal_Bool lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine,
sal_uInt8& rModified, const SvxBorderLine*& rpNew )
{
if (rModified == SC_LINE_DONTCARE)
return sal_False; // weiter geht's nicht
if (rModified == SC_LINE_EMPTY)
{
rModified = SC_LINE_SET;
rpNew = pNewLine;
return sal_True; // zum ersten mal gesetzt
}
if (pOldLine == pNewLine)
{
rpNew = pOldLine;
return sal_False;
}
if (pOldLine && pNewLine)
if (*pOldLine == *pNewLine)
{
rpNew = pOldLine;
return sal_False;
}
rModified = SC_LINE_DONTCARE;
rpNew = NULL;
return sal_True; // andere Linie -> dontcare
}
void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
ScLineFlags& rFlags, const ScPatternAttr* pPattern,
sal_Bool bLeft, SCCOL nDistRight, sal_Bool bTop, SCROW nDistBottom )
{
// rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst:
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, sal_False,
nEndRow - Min( pData[i].nRow, (SCROW)(nEndRow-1) ) );
// nDistBottom hier immer > 0
}
pPattern = GetPattern( nEndRow );
lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, sal_False, 0 );
}
}
//
// Rahmen anwenden
//
// ApplyFrame - auf einen Eintrag im 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 );
// rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst:
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 sal_False;
}
else
{
SfxItemPoolCache aCache( pDocument->GetPool(), &aNewFrame );
ApplyCacheArea( nStartRow, nEndRow, &aCache );
/* ScPatternAttr* pNewPattern = (ScPatternAttr*) pPattern->Clone();
pNewPattern->GetItemSet().Put( aNewFrame );
SetPatternArea( nStartRow, nEndRow, pNewPattern, sal_True );
*/
return sal_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 ) // innerer Teil vorhanden?
{
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, sal_False, nEndRow-nTmpEnd );
nTmpStart = nTmpEnd+1;
if (bChanged)
{
Search(nTmpStart, i);
Search(nEndRow-1, nEndIndex);
}
else
i++;
}
}
ApplyFrame( pLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, sal_False, 0 );
}
}
long lcl_LineSize( const SvxBorderLine& rLine )
{
// nur eine Linie -> halbe Breite, min. 20
// doppelte Linie -> halber Abstand + eine Linie (je 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(),
"Linie hat Abstand, aber nur eine Breite ???" );
// nTotal += ( nDist > 40 ) ? ( nDist / 2 ) : 20;
nTotal += ( nDist > 20 ) ? nDist : 20;
nTotal += ( nWidth > 20 ) ? nWidth : 20;
}
else if (nWidth)
// nTotal += ( nWidth > 40 ) ? ( nWidth / 2 ) : 20;
nTotal += ( nWidth > 20 ) ? nWidth : 20;
//! auch halbieren ???
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 = sal_False;
const SvxBoxItem* pItem = 0;
const SvxBorderLine* pLine = 0;
long nCmp;
// oben
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;
}
// unten
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);
// links
if (bLeft)
{
pLine = pItem->GetLeft();
if (pLine)
{
nCmp = lcl_LineSize(*pLine);
if ( nCmp > rSizes.Left() )
rSizes.Left() = nCmp;
bFound = sal_True;
}
}
// rechts
if (bRight)
{
pLine = pItem->GetRight();
if (pLine)
{
nCmp = lcl_LineSize(*pLine);
if ( nCmp > rSizes.Right() )
rSizes.Right() = nCmp;
bFound = sal_True;
}
}
}
return bFound;
}
// Testen, ob Bereich bestimmtes Attribut enthaelt
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;
}
// Bereich um evtl. enthaltene Zusammenfassungen erweitern
// und evtl. MergeFlag anpassen (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 = sal_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 ); // Daten wurden veraendert
Search( nStartRow, nStartIndex );
Search( nEndRow, nEndIndex );
}
}
}
return bFound;
}
sal_Bool ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow)
{
sal_Bool bFound = sal_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, "was'n hier los?" );
SCCOL nThisCol = nCol;
SCCOL nMergeEndCol = nThisCol + nCountX - 1;
SCROW nMergeEndRow = nThisEnd + nCountY - 1;
//! ApplyAttr fuer Bereiche !!!
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 ); // Daten wurden veraendert !!!
}
++nIndex;
if ( nIndex < nCount )
nThisStart = pData[nIndex-1].nRow+1;
else
nThisStart = MAXROW+1; // Ende
}
return bFound;
}
// Bereich loeschen, aber Merge-Flags stehenlassen
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())
{
// #108045# 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 = sal_False;
else
pDocument->GetPool()->Put( *pWantedPattern ); // im Pool ist es schon!
}
SetPatternArea( nThisRow, nAttrRow, pWantedPattern );
}
Search( nThisRow, nIndex ); // Daten wurden veraendert !!!
}
++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 = sal_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, sal_True );
Search( nThisRow, nIndex ); // Daten wurden veraendert !!!
bChanged = sal_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 = sal_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, sal_True );
Search( nThisRow, nIndex ); // Daten wurden veraendert !!!
bChanged = sal_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, sal_True );
Search( nThisRow, nIndex ); // Daten wurden veraendert !!!
}
++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, sal_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 ); // Daten wurden veraendert !!!
}
else
{
nThisStart = pData[nIndex].nRow + 1; // weiterzaehlen...
++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; // nichts gefunden
--nIndex;
nRet = pData[nIndex].nRow;
}
else
{
nRet = pData[nIndex].nRow+1;
++nIndex;
if (nIndex>=nCount)
return MAXROW+1; // nichts gefunden
}
}
}
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)
{
// for (SCROW nRow = nStart; nRow <= nEnd; nRow++)
// pUsed[nRow] = sal_True;
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; // wegen ++ am Ende
}
}
}
nStart = nEnd + 1;
++nPos;
}
}
sal_Bool ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle,
sal_Bool bGatherAllStyles ) const
{
sal_Bool bIsUsed = sal_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 sal_False;
else
return sal_True;
}
else
return sal_False;
}
//UNUSED2008-05 SCROW ScAttrArray::GetFirstEntryPos() const
//UNUSED2008-05 {
//UNUSED2008-05 DBG_ASSERT( nCount, "nCount = 0" );
//UNUSED2008-05
//UNUSED2008-05 if ( pData[0].pPattern != pDocument->GetDefPattern() )
//UNUSED2008-05 return 0;
//UNUSED2008-05 else
//UNUSED2008-05 {
//UNUSED2008-05 if (nCount==1)
//UNUSED2008-05 return 0; // leer
//UNUSED2008-05 else
//UNUSED2008-05 return pData[0].nRow + 1;
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05
//UNUSED2008-05
//UNUSED2008-05 SCROW ScAttrArray::GetLastEntryPos( sal_Bool bIncludeBottom ) const
//UNUSED2008-05 {
//UNUSED2008-05 DBG_ASSERT( nCount, "nCount == 0" );
//UNUSED2008-05
//UNUSED2008-05 if (bIncludeBottom)
//UNUSED2008-05 bIncludeBottom = ( pData[nCount-1].pPattern != pDocument->GetDefPattern() );
//UNUSED2008-05
//UNUSED2008-05 if (bIncludeBottom)
//UNUSED2008-05 return MAXROW;
//UNUSED2008-05 else
//UNUSED2008-05 {
//UNUSED2008-05 if (nCount<=1)
//UNUSED2008-05 return 0; // leer
//UNUSED2008-05 else
//UNUSED2008-05 return pData[nCount-2].nRow;
//UNUSED2008-05 }
//UNUSED2008-05 }
sal_Bool ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const
{
DBG_ASSERT( nCount, "nCount == 0" );
sal_Bool bFound = sal_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 = sal_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 = sal_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::GetLastAttr( SCROW& rLastRow, SCROW nLastData ) const
{
if ( nLastData == MAXROW )
{
rLastRow = MAXROW;
return sal_True;
}
sal_Bool bFound = sal_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;
while ( nStartPos > 0 && pData[nStartPos - 1].nRow > nLastData &&
pData[nStartPos - 1].pPattern->IsEqual( *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 = sal_False;
}
else if ( !bFound )
{
rLastRow = pData[nEndPos].nRow;
bFound = sal_True;
}
// look further from the top of the range.
nPos = nStartPos;
}
return bFound;
}
sal_Bool ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
{
SCSIZE nIndex;
Search( nStartRow, nIndex );
SCROW nThisStart = nStartRow;
sal_Bool bFound = sal_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
{
//! mit IsVisibleEqual zusammenfassen?
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 zusammengefasste duerfen nicht herausgeschoben werden
// (ob die ganze Zusammenfassung betroffen ist, ist hier nicht zu erkennen)
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 = sal_False; // darf nicht herausgeschoben werden
break;
}
if ( pData[nIndex].nRow >= nEndRow ) // Ende des Bereichs
break;
}
}
return bTest;
}
sal_Bool ScAttrArray::TestInsertRow( SCSIZE nSize ) const
{
// wenn die erste herausgeschobene Zeile vertikal ueberlappt ist,
// wuerde eine kaputte Zusammenfassung uebrigbleiben
if (pData)
{
// MAXROW + 1 - nSize = erste herausgeschobene Zeile
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 sal_False;
}
return sal_True;
}
void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize )
{
if (!pData)
return;
SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // Vorgaenger erweitern
SCSIZE nIndex;
Search( nSearch, nIndex );
// ein gesetztes ScMergeAttr darf nicht ausgedehnt werden
// (darum hinterher wieder loeschen)
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 ) // Ende erreicht ?
{
nNew = MAXROW;
if (!nRemove)
nRemove = i+1; // folgende loeschen
}
pData[i].nRow = nNew;
}
// muessen Eintraege am Ende geloescht werden?
if (nRemove && nRemove < nCount)
DeleteRange( nRemove, nCount-1 );
if (bDoMerge) // ausgedehntes ScMergeAttr wieder reparieren
{
//! ApplyAttr fuer Bereiche !!!
const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE );
for (SCSIZE nAdd=0; nAdd<nSize; nAdd++)
pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef );
// im eingefuegten Bereich ist nichts zusammengefasst
}
// 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 = sal_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;
// unten nicht Default-Pattern nachschieben, um Druckbereiche erkennen zu koennen
// stattdessen nur Merge-Flags loeschen
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 ); // von zusammengefassten auch die Flags loeschen
if ( !HasAttrib( nStartRow, nEndRow, HASATTR_OVERLAPPED | HASATTR_AUTOFILTER) )
SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() );
else
DeleteAreaSafe( nStartRow, nEndRow ); // Merge-Flags stehenlassen
}
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() ) // harte Attribute ?
{
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, sal_False );
else
SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
Search( nThisRow, nIndex ); // Daten wurden veraendert !!!
}
++nIndex;
nThisRow = pData[nIndex-1].nRow+1;
}
}
// Verschieben innerhalb eines Dokuments
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))
{
// Kopieren (bPutToPool=sal_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);
}
// Kopieren zwischen Dokumenten (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 ))
{
// am Default muss nichts veraendert werden
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));
}
}
// Flags stehenlassen
//! mit CopyArea zusammenfassen !!!
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, sal_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 = sal_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), "interner Fehler 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, sal_False );
if (nMarkEnd<rEndRow)
rEndRow = nMarkEnd;
}
}
return sal_True;
}
else
return sal_False;
}
//------------------------------------------------------------------------
//
// Laden / Speichern
//
#if 0
void ScAttrArray::Save( SvStream& /* rStream */ ) const
{
#if SC_ROWLIMIT_STREAM_ACCESS
#error address types changed!
ScWriteHeader aHdr( rStream, 8 );
ScDocumentPool* pDocPool = pDocument->GetPool();
sal_uInt16 nSaveCount = nCount;
SCROW nSaveMaxRow = pDocument->GetSrcMaxRow();
if ( nSaveMaxRow != MAXROW )
{
if ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow )
{
pDocument->SetLostData(); // Warnung ausgeben
do
--nSaveCount;
while ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow );
}
}
rStream << nSaveCount;
const SfxPoolItem* pItem;
for (SCSIZE i=0; i<nSaveCount; i++)
{
rStream << Min( pData[i].nRow, nSaveMaxRow );
const ScPatternAttr* pPattern = pData[i].pPattern;
pDocPool->StoreSurrogate( rStream, pPattern );
// sal_False, weil ATTR_CONDITIONAL (noch) nicht in Vorlagen:
if (pPattern->GetItemSet().GetItemState(ATTR_CONDITIONAL,sal_False,&pItem) == SFX_ITEM_SET)
pDocument->SetConditionalUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
if (pPattern->GetItemSet().GetItemState(ATTR_VALIDDATA,sal_False,&pItem) == SFX_ITEM_SET)
pDocument->SetValidationUsed( ((const SfxUInt32Item*)pItem)->GetValue() );
}
#endif // SC_ROWLIMIT_STREAM_ACCESS
}
void ScAttrArray::Load( SvStream& /* rStream */ )
{
#if SC_ROWLIMIT_STREAM_ACCESS
#error address types changed!
ScDocumentPool* pDocPool = pDocument->GetPool();
ScReadHeader aHdr( rStream );
sal_uInt16 nNewCount;
rStream >> nNewCount;
if ( nNewCount > MAXROW+1 ) // wuerde das Array zu gross?
{
pDocument->SetLostData();
rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
return;
}
Reset( pDocument->GetDefPattern(), sal_False ); // loeschen
pData = new ScAttrEntry[nNewCount]; // neu anlegen
for (SCSIZE i=0; i<nNewCount; i++)
{
rStream >> pData[i].nRow;
sal_uInt16 nWhich = ATTR_PATTERN;
const ScPatternAttr* pNewPattern = (const ScPatternAttr*)
pDocPool->LoadSurrogate( rStream, nWhich, ATTR_PATTERN );
if (!pNewPattern)
{
// da is was schiefgelaufen
DBG_ERROR("ScAttrArray::Load: Surrogat nicht im Pool");
pNewPattern = pDocument->GetDefPattern();
}
ScDocumentPool::CheckRef( *pNewPattern );
pData[i].pPattern = pNewPattern;
// LoadSurrogate erhoeht auch die Ref
}
nCount = nLimit = nNewCount;
if ( nCount > 1 && pData[nCount-2].nRow >= MAXROW ) // faengt ein Attribut hinter MAXROW an?
{
pDocument->SetLostData();
rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
return;
}
if ( pDocument->GetSrcMaxRow() != MAXROW ) // Ende anpassen?
{
// Ende immer auf MAXROW umsetzen (nur auf 32 Bit)
DBG_ASSERT( pData[nCount-1].nRow == pDocument->GetSrcMaxRow(), "Attribut-Ende ?!?" );
pData[nCount-1].nRow = MAXROW;
}
#endif // SC_ROWLIMIT_STREAM_ACCESS
}
#endif
//UNUSED2008-05 void ScAttrArray::ConvertFontsAfterLoad()
//UNUSED2008-05 {
//UNUSED2008-05 ScFontToSubsFontConverter_AutoPtr xFontConverter;
//UNUSED2008-05 const sal_uLong nFlags = FONTTOSUBSFONT_IMPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS;
//UNUSED2008-05 SCSIZE nIndex = 0;
//UNUSED2008-05 SCROW nThisRow = 0;
//UNUSED2008-05
//UNUSED2008-05 while ( nThisRow <= MAXROW )
//UNUSED2008-05 {
//UNUSED2008-05 const ScPatternAttr* pOldPattern = pData[nIndex].pPattern;
//UNUSED2008-05 const SfxPoolItem* pItem;
//UNUSED2008-05 if( pOldPattern->GetItemSet().GetItemState( ATTR_FONT, sal_False, &pItem ) == SFX_ITEM_SET )
//UNUSED2008-05 {
//UNUSED2008-05 const SvxFontItem* pFontItem = (const SvxFontItem*) pItem;
//UNUSED2008-05 const String& rOldName = pFontItem->GetFamilyName();
//UNUSED2008-05 xFontConverter = CreateFontToSubsFontConverter( rOldName, nFlags );
//UNUSED2008-05 if ( xFontConverter )
//UNUSED2008-05 {
//UNUSED2008-05 String aNewName( GetFontToSubsFontName( xFontConverter ) );
//UNUSED2008-05 if ( aNewName != rOldName )
//UNUSED2008-05 {
//UNUSED2008-05 SCROW nAttrRow = pData[nIndex].nRow;
//UNUSED2008-05 SvxFontItem aNewItem( pFontItem->GetFamily(), aNewName,
//UNUSED2008-05 pFontItem->GetStyleName(), pFontItem->GetPitch(),
//UNUSED2008-05 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT );
//UNUSED2008-05 ScPatternAttr aNewPattern( *pOldPattern );
//UNUSED2008-05 aNewPattern.GetItemSet().Put( aNewItem );
//UNUSED2008-05 SetPatternArea( nThisRow, nAttrRow, &aNewPattern, sal_True );
//UNUSED2008-05 Search( nThisRow, nIndex ); //! data changed
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05 }
//UNUSED2008-05 ++nIndex;
//UNUSED2008-05 nThisRow = pData[nIndex-1].nRow+1;
//UNUSED2008-05 }
//UNUSED2008-05 }
SCSIZE ScAttrArray::Count( SCROW nStartRow, SCROW nEndRow )
{
SCSIZE nIndex1, nIndex2;
if( !Search( nStartRow, nIndex1 ) )
return 0;
if( !Search( nEndRow, nIndex2 ) )
nIndex2 = this->nCount - 1;
return nIndex2 - nIndex1 + 1;
}