blob: 7bd6a7918ad8410af6447e3a7d8691f90e4a3751 [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 ---------------------------------------------------------------
#include <map>
#include <svl/poolcach.hxx>
#include <svl/zforlist.hxx>
#include <editeng/scripttypeitem.hxx>
#include <string.h>
#include "scitems.hxx"
#include "column.hxx"
#include "cell.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "attarray.hxx"
#include "patattr.hxx"
#include "compiler.hxx"
#include "brdcst.hxx"
#include "markdata.hxx"
#include "detfunc.hxx" // for Notes in Sort/Swap
#include "postit.hxx"
//#pragma optimize ( "", off )
// nur Search ohne Optimierung!
// STATIC DATA -----------------------------------------------------------
using namespace formula;
inline sal_Bool IsAmbiguousScriptNonZero( sal_uInt8 nScript )
{
//! move to a header file
return ( nScript != SCRIPTTYPE_LATIN &&
nScript != SCRIPTTYPE_ASIAN &&
nScript != SCRIPTTYPE_COMPLEX &&
nScript != 0 );
}
// -----------------------------------------------------------------------------------------
ScColumn::ScColumn() :
nCol( 0 ),
nCount( 0 ),
nLimit( 0 ),
pItems( NULL ),
pAttrArray( NULL ),
pDocument( NULL )
{
}
ScColumn::~ScColumn()
{
FreeAll();
if (pAttrArray) delete pAttrArray;
}
void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc)
{
nCol = nNewCol;
nTab = nNewTab;
pDocument = pDoc;
pAttrArray = new ScAttrArray( nCol, nTab, pDocument );
}
SCsROW ScColumn::GetNextUnprotected( SCROW nRow, sal_Bool bUp ) const
{
return pAttrArray->GetNextUnprotected(nRow, bUp);
}
sal_uInt16 ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const
{
// nix:0, mitte:1, unten:2, links:4, oben:8, rechts:16, offen:32
if ( !pItems )
return 0;
if ( nRow1 == nRow2 )
{
SCSIZE nIndex;
if ( Search( nRow1, nIndex ) )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA
&& ((ScFormulaCell*)pCell)->GetMatrixFlag() )
{
ScAddress aOrg( ScAddress::INITIALIZE_INVALID );
return ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg );
}
}
return 0;
}
else
{
ScAddress aOrg( ScAddress::INITIALIZE_INVALID );
sal_Bool bOpen = sal_False;
sal_uInt16 nEdges = 0;
SCSIZE nIndex;
Search( nRow1, nIndex );
while ( nIndex < nCount && pItems[nIndex].nRow <= nRow2 )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA
&& ((ScFormulaCell*)pCell)->GetMatrixFlag() )
{
nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg );
if ( nEdges )
{
if ( nEdges & 8 )
bOpen = sal_True; // obere Kante oeffnet, weitersehen
else if ( !bOpen )
return nEdges | 32; // es gibt was, was nicht geoeffnet wurde
else if ( nEdges & 1 )
return nEdges; // mittendrin
// (nMask & 16 und (4 und nicht 16)) oder
// (nMask & 4 und (16 und nicht 4))
if ( ((nMask & 16) && (nEdges & 4) && !(nEdges & 16))
|| ((nMask & 4) && (nEdges & 16) && !(nEdges & 4)) )
return nEdges; // nur linke/rechte Kante
if ( nEdges & 2 )
bOpen = sal_False; // untere Kante schliesst
}
}
nIndex++;
}
if ( bOpen )
nEdges |= 32; // es geht noch weiter
return nEdges;
}
}
sal_Bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark) const
{
if ( rMark.IsMultiMarked() )
{
sal_Bool bFound = sal_False;
ScAddress aOrg( ScAddress::INITIALIZE_INVALID );
ScAddress aCurOrg( ScAddress::INITIALIZE_INVALID );
SCROW nTop, nBottom;
ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol );
while ( !bFound && aMarkIter.Next( nTop, nBottom ) )
{
sal_Bool bOpen = sal_False;
sal_uInt16 nEdges;
SCSIZE nIndex;
Search( nTop, nIndex );
while ( !bFound && nIndex < nCount && pItems[nIndex].nRow <= nBottom )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA
&& ((ScFormulaCell*)pCell)->GetMatrixFlag() )
{
nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg );
if ( nEdges )
{
if ( nEdges & 8 )
bOpen = sal_True; // obere Kante oeffnet, weitersehen
else if ( !bOpen )
return sal_True; // es gibt was, was nicht geoeffnet wurde
else if ( nEdges & 1 )
bFound = sal_True; // mittendrin, alles selektiert?
// (4 und nicht 16) oder (16 und nicht 4)
if ( (((nEdges & 4) | 16) ^ ((nEdges & 16) | 4)) )
bFound = sal_True; // nur linke/rechte Kante, alles selektiert?
if ( nEdges & 2 )
bOpen = sal_False; // untere Kante schliesst
if ( bFound )
{ // alles selektiert?
if ( aCurOrg != aOrg )
{ // neue Matrix zu pruefen?
aCurOrg = aOrg;
ScFormulaCell* pFCell;
if ( ((ScFormulaCell*)pCell)->GetMatrixFlag()
== MM_REFERENCE )
pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg );
else
pFCell = (ScFormulaCell*)pCell;
SCCOL nC;
SCROW nR;
pFCell->GetMatColsRows( nC, nR );
ScRange aRange( aOrg, ScAddress(
aOrg.Col() + nC - 1, aOrg.Row() + nR - 1,
aOrg.Tab() ) );
if ( rMark.IsAllMarked( aRange ) )
bFound = sal_False;
}
else
bFound = sal_False; // war schon
}
}
}
nIndex++;
}
if ( bOpen )
return sal_True;
}
return bFound;
}
else
return sal_False;
}
//UNUSED2009-05 sal_Bool ScColumn::HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes,
//UNUSED2009-05 sal_Bool bLeft, sal_Bool bRight ) const
//UNUSED2009-05 {
//UNUSED2009-05 return pAttrArray->HasLines( nRow1, nRow2, rSizes, bLeft, bRight );
//UNUSED2009-05 }
bool ScColumn::HasAttrib( SCROW nRow1, SCROW nRow2, sal_uInt16 nMask ) const
{
return pAttrArray->HasAttrib( nRow1, nRow2, nMask );
}
sal_Bool ScColumn::HasAttribSelection( const ScMarkData& rMark, sal_uInt16 nMask ) const
{
sal_Bool bFound = sal_False;
SCROW nTop;
SCROW nBottom;
if (rMark.IsMultiMarked())
{
ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol );
while (aMarkIter.Next( nTop, nBottom ) && !bFound)
{
if (pAttrArray->HasAttrib( nTop, nBottom, nMask ))
bFound = sal_True;
}
}
return bFound;
}
sal_Bool ScColumn::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow,
SCCOL& rPaintCol, SCROW& rPaintRow,
sal_Bool bRefresh, sal_Bool bAttrs )
{
return pAttrArray->ExtendMerge( nThisCol, nStartRow, nEndRow, rPaintCol, rPaintRow, bRefresh, bAttrs );
}
void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, sal_Bool bDeep ) const
{
SCROW nTop;
SCROW nBottom;
if ( rMark.IsMultiMarked() )
{
const ScMarkArray* pArray = rMark.GetArray() + nCol;
if ( pArray->HasMarks() )
{
ScMarkArrayIter aMarkIter( pArray );
while (aMarkIter.Next( nTop, nBottom ))
pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep );
}
}
}
void ScColumn::MergePatternArea( ScMergePatternState& rState, SCROW nRow1, SCROW nRow2, sal_Bool bDeep ) const
{
pAttrArray->MergePatternArea( nRow1, nRow2, rState, bDeep );
}
void ScColumn::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner,
ScLineFlags& rFlags,
SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight ) const
{
pAttrArray->MergeBlockFrame( pLineOuter, pLineInner, rFlags, nStartRow, nEndRow, bLeft, nDistRight );
}
void ScColumn::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner,
SCROW nStartRow, SCROW nEndRow, sal_Bool bLeft, SCCOL nDistRight )
{
pAttrArray->ApplyBlockFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight );
}
const ScPatternAttr* ScColumn::GetPattern( SCROW nRow ) const
{
return pAttrArray->GetPattern( nRow );
}
const ScPatternAttr* ScColumn::GetPatternRange( SCROW& rStartRow, SCROW& rEndRow, SCROW nRow ) const
{
return pAttrArray->GetPatternRange( rStartRow, rEndRow, nRow );
}
const SfxPoolItem* ScColumn::GetAttr( SCROW nRow, sal_uInt16 nWhich ) const
{
return &pAttrArray->GetPattern( nRow )->GetItemSet().Get(nWhich);
}
const ScPatternAttr* ScColumn::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const
{
::std::map< const ScPatternAttr*, size_t > aAttrMap;
const ScPatternAttr* pMaxPattern = 0;
size_t nMaxCount = 0;
ScAttrIterator aAttrIter( pAttrArray, nStartRow, nEndRow );
const ScPatternAttr* pPattern;
SCROW nAttrRow1 = 0, nAttrRow2 = 0;
while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != 0 )
{
size_t& rnCount = aAttrMap[ pPattern ];
rnCount += (nAttrRow2 - nAttrRow1 + 1);
if( rnCount > nMaxCount )
{
pMaxPattern = pPattern;
nMaxCount = rnCount;
}
}
return pMaxPattern;
}
sal_uLong ScColumn::GetNumberFormat( SCROW nRow ) const
{
return pAttrArray->GetPattern( nRow )->GetNumberFormat( pDocument->GetFormatTable() );
}
SCsROW ScColumn::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark )
{
SCROW nTop = 0;
SCROW nBottom = 0;
sal_Bool bFound = sal_False;
if ( rMark.IsMultiMarked() )
{
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
while (aMarkIter.Next( nTop, nBottom ))
{
pAttrArray->ApplyCacheArea( nTop, nBottom, pCache );
bFound = sal_True;
}
}
if (!bFound)
return -1;
else if (nTop==0 && nBottom==MAXROW)
return 0;
else
return nBottom;
}
void ScColumn::ChangeSelectionIndent( sal_Bool bIncrement, const ScMarkData& rMark )
{
SCROW nTop;
SCROW nBottom;
if ( pAttrArray && rMark.IsMultiMarked() )
{
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
while (aMarkIter.Next( nTop, nBottom ))
pAttrArray->ChangeIndent(nTop, nBottom, bIncrement);
}
}
void ScColumn::ClearSelectionItems( const sal_uInt16* pWhich,const ScMarkData& rMark )
{
SCROW nTop;
SCROW nBottom;
if ( pAttrArray && rMark.IsMultiMarked() )
{
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
while (aMarkIter.Next( nTop, nBottom ))
pAttrArray->ClearItems(nTop, nBottom, pWhich);
}
}
void ScColumn::DeleteSelection( sal_uInt16 nDelFlag, const ScMarkData& rMark )
{
SCROW nTop;
SCROW nBottom;
if ( rMark.IsMultiMarked() )
{
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
while (aMarkIter.Next( nTop, nBottom ))
DeleteArea(nTop, nBottom, nDelFlag);
}
}
void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr )
{
const SfxItemSet* pSet = &rPatAttr.GetItemSet();
SfxItemPoolCache aCache( pDocument->GetPool(), pSet );
const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
// sal_True = alten Eintrag behalten
ScPatternAttr* pNewPattern = (ScPatternAttr*) &aCache.ApplyTo( *pPattern, sal_True );
ScDocumentPool::CheckRef( *pPattern );
ScDocumentPool::CheckRef( *pNewPattern );
if (pNewPattern != pPattern)
pAttrArray->SetPattern( nRow, pNewPattern );
}
void ScColumn::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr )
{
const SfxItemSet* pSet = &rPatAttr.GetItemSet();
SfxItemPoolCache aCache( pDocument->GetPool(), pSet );
pAttrArray->ApplyCacheArea( nStartRow, nEndRow, &aCache );
}
void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
const ScPatternAttr& rPattern, short nNewType )
{
const SfxItemSet* pSet = &rPattern.GetItemSet();
SfxItemPoolCache aCache( pDocument->GetPool(), pSet );
SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
SCROW nEndRow = rRange.aEnd.Row();
for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ )
{
SCROW nRow1, nRow2;
const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(
nRow1, nRow2, nRow );
sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter );
short nOldType = pFormatter->GetType( nFormat );
if ( nOldType == nNewType || pFormatter->IsCompatible( nOldType, nNewType ) )
nRow = nRow2;
else
{
SCROW nNewRow1 = Max( nRow1, nRow );
SCROW nNewRow2 = Min( nRow2, nEndRow );
pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, &aCache );
nRow = nNewRow2;
}
}
}
void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet& rStyle )
{
const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow);
ScPatternAttr* pNewPattern = new ScPatternAttr(*pPattern);
if (pNewPattern)
{
pNewPattern->SetStyleSheet((ScStyleSheet*)&rStyle);
pAttrArray->SetPattern(nRow, pNewPattern, sal_True);
delete pNewPattern;
}
}
void ScColumn::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle )
{
pAttrArray->ApplyStyleArea(nStartRow, nEndRow, (ScStyleSheet*)&rStyle);
}
void ScColumn::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
{
SCROW nTop;
SCROW nBottom;
if ( rMark.IsMultiMarked() )
{
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
while (aMarkIter.Next( nTop, nBottom ))
pAttrArray->ApplyStyleArea(nTop, nBottom, (ScStyleSheet*)&rStyle);
}
}
void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark,
const SvxBorderLine* pLine, sal_Bool bColorOnly )
{
if ( bColorOnly && !pLine )
return;
SCROW nTop;
SCROW nBottom;
if (rMark.IsMultiMarked())
{
ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol );
while (aMarkIter.Next( nTop, nBottom ))
pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly );
}
}
const ScStyleSheet* ScColumn::GetStyle( SCROW nRow ) const
{
return pAttrArray->GetPattern( nRow )->GetStyleSheet();
}
const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, sal_Bool& rFound ) const
{
rFound = sal_False;
if (!rMark.IsMultiMarked())
{
DBG_ERROR("ScColumn::GetSelectionStyle ohne Selektion");
return NULL;
}
sal_Bool bEqual = sal_True;
const ScStyleSheet* pStyle = NULL;
const ScStyleSheet* pNewStyle;
ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol );
SCROW nTop;
SCROW nBottom;
while (bEqual && aMarkIter.Next( nTop, nBottom ))
{
ScAttrIterator aAttrIter( pAttrArray, nTop, nBottom );
SCROW nRow;
SCROW nDummy;
const ScPatternAttr* pPattern;
while (bEqual && ( pPattern = aAttrIter.Next( nRow, nDummy ) ) != NULL)
{
pNewStyle = pPattern->GetStyleSheet();
rFound = sal_True;
if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
bEqual = sal_False; // unterschiedliche
pStyle = pNewStyle;
}
}
return bEqual ? pStyle : NULL;
}
const ScStyleSheet* ScColumn::GetAreaStyle( sal_Bool& rFound, SCROW nRow1, SCROW nRow2 ) const
{
rFound = sal_False;
sal_Bool bEqual = sal_True;
const ScStyleSheet* pStyle = NULL;
const ScStyleSheet* pNewStyle;
ScAttrIterator aAttrIter( pAttrArray, nRow1, nRow2 );
SCROW nRow;
SCROW nDummy;
const ScPatternAttr* pPattern;
while (bEqual && ( pPattern = aAttrIter.Next( nRow, nDummy ) ) != NULL)
{
pNewStyle = pPattern->GetStyleSheet();
rFound = sal_True;
if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
bEqual = sal_False; // unterschiedliche
pStyle = pNewStyle;
}
return bEqual ? pStyle : NULL;
}
void ScColumn::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, ScFlatBoolRowSegments& rUsedRows, bool bReset )
{
pAttrArray->FindStyleSheet( pStyleSheet, rUsedRows, bReset );
}
sal_Bool ScColumn::IsStyleSheetUsed( const ScStyleSheet& rStyle, sal_Bool bGatherAllStyles ) const
{
return pAttrArray->IsStyleSheetUsed( rStyle, bGatherAllStyles );
}
sal_Bool ScColumn::ApplyFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags )
{
return pAttrArray->ApplyFlags( nStartRow, nEndRow, nFlags );
}
sal_Bool ScColumn::RemoveFlags( SCROW nStartRow, SCROW nEndRow, sal_Int16 nFlags )
{
return pAttrArray->RemoveFlags( nStartRow, nEndRow, nFlags );
}
void ScColumn::ClearItems( SCROW nStartRow, SCROW nEndRow, const sal_uInt16* pWhich )
{
pAttrArray->ClearItems( nStartRow, nEndRow, pWhich );
}
void ScColumn::SetPattern( SCROW nRow, const ScPatternAttr& rPatAttr, sal_Bool bPutToPool )
{
pAttrArray->SetPattern( nRow, &rPatAttr, bPutToPool );
}
void ScColumn::SetPatternArea( SCROW nStartRow, SCROW nEndRow,
const ScPatternAttr& rPatAttr, sal_Bool bPutToPool )
{
pAttrArray->SetPatternArea( nStartRow, nEndRow, &rPatAttr, bPutToPool );
}
void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
{
// um nur ein neues SetItem zu erzeugen, brauchen wir keinen SfxItemPoolCache.
//! Achtung: der SfxItemPoolCache scheint zuviele Refs fuer das neue SetItem zu erzeugen ??
ScDocumentPool* pDocPool = pDocument->GetPool();
const ScPatternAttr* pOldPattern = pAttrArray->GetPattern( nRow );
ScPatternAttr* pTemp = new ScPatternAttr(*pOldPattern);
pTemp->GetItemSet().Put(rAttr);
const ScPatternAttr* pNewPattern = (const ScPatternAttr*) &pDocPool->Put( *pTemp );
if ( pNewPattern != pOldPattern )
pAttrArray->SetPattern( nRow, pNewPattern );
else
pDocPool->Remove( *pNewPattern ); // ausser Spesen nichts gewesen
delete pTemp;
// alte Version mit SfxItemPoolCache:
#if 0
SfxItemPoolCache aCache( pDocument->GetPool(), &rAttr );
const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
// sal_True = alten Eintrag behalten
ScPatternAttr* pNewPattern = (ScPatternAttr*) &aCache.ApplyTo( *pPattern, sal_True );
ScDocumentPool::CheckRef( *pPattern );
ScDocumentPool::CheckRef( *pNewPattern );
if (pNewPattern != pPattern)
pAttrArray->SetPattern( nRow, pNewPattern );
#endif
}
#ifdef _MSC_VER
#pragma optimize ( "", off )
#endif
sal_Bool ScColumn::Search( SCROW nRow, SCSIZE& nIndex ) const
{
if ( !pItems || !nCount )
{
nIndex = 0;
return sal_False;
}
SCROW nMinRow = pItems[0].nRow;
if ( nRow <= nMinRow )
{
nIndex = 0;
return nRow == nMinRow;
}
SCROW nMaxRow = pItems[nCount-1].nRow;
if ( nRow >= nMaxRow )
{
if ( nRow == nMaxRow )
{
nIndex = nCount - 1;
return sal_True;
}
else
{
nIndex = nCount;
return sal_False;
}
}
long nOldLo, nOldHi;
long nLo = nOldLo = 0;
long nHi = nOldHi = Min(static_cast<long>(nCount)-1, static_cast<long>(nRow) );
long i = 0;
sal_Bool bFound = sal_False;
// quite continuous distribution? => interpolating search
sal_Bool bInterpol = (static_cast<SCSIZE>(nMaxRow - nMinRow) < nCount * 2);
SCROW nR;
while ( !bFound && nLo <= nHi )
{
if ( !bInterpol || nHi - nLo < 3 )
i = (nLo+nHi) / 2; // no effort, no division by zero
else
{ // interpolating search
long nLoRow = pItems[nLo].nRow; // no unsigned underflow upon substraction
i = nLo + (long)((long)(nRow - nLoRow) * (nHi - nLo)
/ (pItems[nHi].nRow - nLoRow));
if ( i < 0 || static_cast<SCSIZE>(i) >= nCount )
{ // oops ...
i = (nLo+nHi) / 2;
bInterpol = sal_False;
}
}
nR = pItems[i].nRow;
if ( nR < nRow )
{
nLo = i+1;
if ( bInterpol )
{
if ( nLo <= nOldLo )
bInterpol = sal_False;
else
nOldLo = nLo;
}
}
else
{
if ( nR > nRow )
{
nHi = i-1;
if ( bInterpol )
{
if ( nHi >= nOldHi )
bInterpol = sal_False;
else
nOldHi = nHi;
}
}
else
bFound = sal_True;
}
}
if (bFound)
nIndex = static_cast<SCSIZE>(i);
else
nIndex = static_cast<SCSIZE>(nLo); // rear index
return bFound;
}
#ifdef _MSC_VER
#pragma optimize ( "", on )
#endif
ScBaseCell* ScColumn::GetCell( SCROW nRow ) const
{
SCSIZE nIndex;
if (Search(nRow, nIndex))
return pItems[nIndex].pCell;
return NULL;
}
void ScColumn::Resize( SCSIZE nSize )
{
if (nSize > sal::static_int_cast<SCSIZE>(MAXROWCOUNT))
nSize = MAXROWCOUNT;
if (nSize < nCount)
nSize = nCount;
ColEntry* pNewItems;
if (nSize)
{
SCSIZE nNewSize = nSize + COLUMN_DELTA - 1;
nNewSize -= nNewSize % COLUMN_DELTA;
nLimit = nNewSize;
pNewItems = new ColEntry[nLimit];
}
else
{
nLimit = 0;
pNewItems = NULL;
}
if (pItems)
{
if (pNewItems)
memmove( pNewItems, pItems, nCount * sizeof(ColEntry) );
delete[] pItems;
}
pItems = pNewItems;
}
// SwapRow zum Sortieren
namespace {
/** Moves broadcaster from old cell to new cell if exists, otherwise creates a new note cell. */
void lclTakeBroadcaster( ScBaseCell*& rpCell, SvtBroadcaster* pBC )
{
if( pBC )
{
if( rpCell )
rpCell->TakeBroadcaster( pBC );
else
rpCell = new ScNoteCell( pBC );
}
}
} // namespace
void ScColumn::SwapRow(SCROW nRow1, SCROW nRow2)
{
/* Simple swap of cell pointers does not work if broadcasters exist (crash
if cell broadcasts directly or indirectly to itself). While swapping
the cells, broadcasters have to remain at old positions! */
/* While cloning cells, do not clone notes, but move note pointers to new
cells. This prevents creation of new caption drawing objects for every
swap operation while sorting. */
ScBaseCell* pCell1 = 0;
SCSIZE nIndex1;
if ( Search( nRow1, nIndex1 ) )
pCell1 = pItems[nIndex1].pCell;
ScBaseCell* pCell2 = 0;
SCSIZE nIndex2;
if ( Search( nRow2, nIndex2 ) )
pCell2 = pItems[nIndex2].pCell;
// no cells found, nothing to do
if ( !pCell1 && !pCell2 )
return ;
// swap variables if first cell is empty, to save some code below
if ( !pCell1 )
{
::std::swap( nRow1, nRow2 );
::std::swap( nIndex1, nIndex2 );
::std::swap( pCell1, pCell2 );
}
// from here: first cell (pCell1, nIndex1) exists always
ScAddress aPos1( nCol, nRow1, nTab );
ScAddress aPos2( nCol, nRow2, nTab );
CellType eType1 = pCell1->GetCellType();
CellType eType2 = pCell2 ? pCell2->GetCellType() : CELLTYPE_NONE;
ScFormulaCell* pFmlaCell1 = (eType1 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0;
ScFormulaCell* pFmlaCell2 = (eType2 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell2 ) : 0;
// simple swap if no formula cells present
if ( !pFmlaCell1 && !pFmlaCell2 )
{
// remember cell broadcasters, must remain at old position
SvtBroadcaster* pBC1 = pCell1->ReleaseBroadcaster();
if ( pCell2 )
{
/* Both cells exist, no formula cells involved, a simple swap can
be performed (but keep broadcasters and notes at old position). */
pItems[nIndex1].pCell = pCell2;
pItems[nIndex2].pCell = pCell1;
SvtBroadcaster* pBC2 = pCell2->ReleaseBroadcaster();
pCell1->TakeBroadcaster( pBC2 );
pCell2->TakeBroadcaster( pBC1 );
}
else
{
ScNoteCell* pDummyCell = pBC1 ? new ScNoteCell( pBC1 ) : 0;
if ( pDummyCell )
{
// insert dummy note cell (without note) containing old broadcaster
pItems[nIndex1].pCell = pDummyCell;
}
else
{
// remove ColEntry at old position
--nCount;
memmove( &pItems[nIndex1], &pItems[nIndex1 + 1], (nCount - nIndex1) * sizeof(ColEntry) );
pItems[nCount].nRow = 0;
pItems[nCount].pCell = 0;
}
// insert ColEntry at new position
Insert( nRow2, pCell1 );
}
return;
}
// from here: at least one of the cells is a formula cell
/* Never move any array formulas. Disabling sort if parts of array
formulas are contained is done at UI. */
if ( (pFmlaCell1 && (pFmlaCell1->GetMatrixFlag() != 0)) || (pFmlaCell2 && (pFmlaCell2->GetMatrixFlag() != 0)) )
return;
// do not swap, if formulas are equal
if ( pFmlaCell1 && pFmlaCell2 )
{
ScTokenArray* pCode1 = pFmlaCell1->GetCode();
ScTokenArray* pCode2 = pFmlaCell2->GetCode();
if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN
{
sal_Bool bEqual = sal_True;
sal_uInt16 nLen = pCode1->GetLen();
FormulaToken** ppToken1 = pCode1->GetArray();
FormulaToken** ppToken2 = pCode2->GetArray();
for (sal_uInt16 i=0; i<nLen; i++)
{
if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) ||
ppToken1[i]->Is3DRef() || ppToken2[i]->Is3DRef() )
{
bEqual = sal_False;
break;
}
}
// do not swap formula cells with equal formulas, but swap notes
if (bEqual)
{
ScPostIt* pNote1 = pCell1->ReleaseNote();
pCell1->TakeNote( pCell2->ReleaseNote() );
pCell2->TakeNote( pNote1 );
return;
}
}
}
// hier kein UpdateReference wegen #30529# - mitsortiert werden nur noch relative Referenzen
// long dy = (long)nRow2 - (long)nRow1;
/* Create clone of pCell1 at position of pCell2 (pCell1 exists always, see
variable swapping above). Do not clone the note, but move pointer of
old note to new cell. */
ScBaseCell* pNew2 = pCell1->CloneWithoutNote( *pDocument, aPos2, SC_CLONECELL_ADJUST3DREL );
pNew2->TakeNote( pCell1->ReleaseNote() );
/* Create clone of pCell2 at position of pCell1. Do not clone the note,
but move pointer of old note to new cell. */
ScBaseCell* pNew1 = 0;
if ( pCell2 )
{
pNew1 = pCell2->CloneWithoutNote( *pDocument, aPos1, SC_CLONECELL_ADJUST3DREL );
pNew1->TakeNote( pCell2->ReleaseNote() );
}
// move old broadcasters new cells at the same old position
SvtBroadcaster* pBC1 = pCell1->ReleaseBroadcaster();
lclTakeBroadcaster( pNew1, pBC1 );
SvtBroadcaster* pBC2 = pCell2 ? pCell2->ReleaseBroadcaster() : 0;
lclTakeBroadcaster( pNew2, pBC2 );
/* Insert the new cells. Old cell has to be deleted, if there is no new
cell (call to Insert deletes old cell by itself). */
if ( !pNew1 )
Delete( nRow1 ); // deletes pCell1
else
Insert( nRow1, pNew1 ); // deletes pCell1, inserts pNew1
if ( pCell2 && !pNew2 )
Delete( nRow2 ); // deletes pCell2
else if ( pNew2 )
Insert( nRow2, pNew2 ); // deletes pCell2 (if existing), inserts pNew2
}
void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol)
{
ScBaseCell* pCell1 = 0;
SCSIZE nIndex1;
if ( Search( nRow, nIndex1 ) )
pCell1 = pItems[nIndex1].pCell;
ScBaseCell* pCell2 = 0;
SCSIZE nIndex2;
if ( rCol.Search( nRow, nIndex2 ) )
pCell2 = rCol.pItems[nIndex2].pCell;
// reverse call if own cell is missing (ensures own existing cell in following code)
if( !pCell1 )
{
if( pCell2 )
rCol.SwapCell( nRow, *this );
return;
}
// from here: own cell (pCell1, nIndex1) exists always
ScFormulaCell* pFmlaCell1 = (pCell1->GetCellType() == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0;
ScFormulaCell* pFmlaCell2 = (pCell2 && (pCell2->GetCellType() == CELLTYPE_FORMULA)) ? static_cast< ScFormulaCell* >( pCell2 ) : 0;
if ( pCell2 )
{
// Tauschen
pItems[nIndex1].pCell = pCell2;
rCol.pItems[nIndex2].pCell = pCell1;
// Referenzen aktualisieren
SCsCOL dx = rCol.nCol - nCol;
if ( pFmlaCell1 )
{
ScRange aRange( ScAddress( rCol.nCol, 0, nTab ),
ScAddress( rCol.nCol, MAXROW, nTab ) );
pFmlaCell1->aPos.SetCol( rCol.nCol );
pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0);
}
if ( pFmlaCell2 )
{
ScRange aRange( ScAddress( nCol, 0, nTab ),
ScAddress( nCol, MAXROW, nTab ) );
pFmlaCell2->aPos.SetCol( nCol );
pFmlaCell2->UpdateReference(URM_MOVE, aRange, -dx, 0, 0);
}
}
else
{
// Loeschen
--nCount;
memmove( &pItems[nIndex1], &pItems[nIndex1 + 1], (nCount - nIndex1) * sizeof(ColEntry) );
pItems[nCount].nRow = 0;
pItems[nCount].pCell = 0;
// Referenzen aktualisieren
SCsCOL dx = rCol.nCol - nCol;
if ( pFmlaCell1 )
{
ScRange aRange( ScAddress( rCol.nCol, 0, nTab ),
ScAddress( rCol.nCol, MAXROW, nTab ) );
pFmlaCell1->aPos.SetCol( rCol.nCol );
pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0);
}
// Einfuegen
rCol.Insert(nRow, pCell1);
}
}
sal_Bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
{
if (!IsEmpty())
{
sal_Bool bTest = sal_True;
if (pItems)
for (SCSIZE i=0; (i<nCount) && bTest; i++)
bTest = (pItems[i].nRow < nStartRow) || (pItems[i].nRow > nEndRow)
|| pItems[i].pCell->IsBlank();
// AttrArray testet nur zusammengefasste
if ((bTest) && (pAttrArray))
bTest = pAttrArray->TestInsertCol(nStartRow, nEndRow);
//! rausgeschobene Attribute bei Undo beruecksichtigen
return bTest;
}
else
return sal_True;
}
sal_Bool ScColumn::TestInsertRow( SCSIZE nSize ) const
{
// AttrArray only looks for merged cells
if ( pItems && nCount )
return ( nSize <= sal::static_int_cast<SCSIZE>(MAXROW) &&
pItems[nCount-1].nRow <= MAXROW-(SCROW)nSize && pAttrArray->TestInsertRow( nSize ) );
else
return pAttrArray->TestInsertRow( nSize );
#if 0
//! rausgeschobene Attribute bei Undo beruecksichtigen
if ( nSize > static_cast<SCSIZE>(MAXROW) )
return sal_False;
SCSIZE nVis = nCount;
while ( nVis && pItems[nVis-1].pCell->IsBlank() )
--nVis;
if ( nVis )
return ( pItems[nVis-1].nRow <= MAXROW-nSize );
else
return sal_True;
#endif
}
void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
{
pAttrArray->InsertRow( nStartRow, nSize );
//! Search
if ( !pItems || !nCount )
return;
SCSIZE i;
Search( nStartRow, i );
if ( i >= nCount )
return ;
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // Mehrfachberechnungen vermeiden
SCSIZE nNewCount = nCount;
sal_Bool bCountChanged = sal_False;
ScAddress aAdr( nCol, 0, nTab );
ScHint aHint( SC_HINT_DATACHANGED, aAdr, NULL ); // only areas (ScBaseCell* == NULL)
ScAddress& rAddress = aHint.GetAddress();
// for sparse occupation use single broadcasts, not ranges
sal_Bool bSingleBroadcasts = (((pItems[nCount-1].nRow - pItems[i].nRow) /
(nCount - i)) > 1);
if ( bSingleBroadcasts )
{
SCROW nLastBroadcast = MAXROW+1;
for ( ; i < nCount; i++)
{
SCROW nOldRow = pItems[i].nRow;
// #43940# Aenderung Quelle broadcasten
if ( nLastBroadcast != nOldRow )
{ // direkt aufeinanderfolgende nicht doppelt broadcasten
rAddress.SetRow( nOldRow );
pDocument->AreaBroadcast( aHint );
}
SCROW nNewRow = (pItems[i].nRow += nSize);
// #43940# Aenderung Ziel broadcasten
rAddress.SetRow( nNewRow );
pDocument->AreaBroadcast( aHint );
nLastBroadcast = nNewRow;
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow );
if ( nNewRow > MAXROW && !bCountChanged )
{
nNewCount = i;
bCountChanged = sal_True;
}
}
}
else
{
rAddress.SetRow( pItems[i].nRow );
ScRange aRange( rAddress );
for ( ; i < nCount; i++)
{
SCROW nNewRow = (pItems[i].nRow += nSize);
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow );
if ( nNewRow > MAXROW && !bCountChanged )
{
nNewCount = i;
bCountChanged = sal_True;
aRange.aEnd.SetRow( MAXROW );
}
}
if ( !bCountChanged )
aRange.aEnd.SetRow( pItems[nCount-1].nRow );
pDocument->AreaBroadcastInRange( aRange, aHint );
}
if (bCountChanged)
{
SCSIZE nDelCount = nCount - nNewCount;
ScBaseCell** ppDelCells = new ScBaseCell*[nDelCount];
SCROW* pDelRows = new SCROW[nDelCount];
for (i = 0; i < nDelCount; i++)
{
ppDelCells[i] = pItems[nNewCount+i].pCell;
pDelRows[i] = pItems[nNewCount+i].nRow;
}
nCount = nNewCount;
for (i = 0; i < nDelCount; i++)
{
ScBaseCell* pCell = ppDelCells[i];
DBG_ASSERT( pCell->IsBlank(), "sichtbare Zelle weggeschoben" );
SvtBroadcaster* pBC = pCell->GetBroadcaster();
if (pBC)
{
MoveListeners( *pBC, pDelRows[i] - nSize );
pCell->DeleteBroadcaster();
pCell->Delete();
}
}
delete [] pDelRows;
delete [] ppDelCells;
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, sal_Bool bKeepScenarioFlags, sal_Bool bCloneNoteCaptions)
{
pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
bKeepScenarioFlags ? (SC_MF_ALL & ~SC_MF_SCENARIO) : SC_MF_ALL );
SCSIZE i;
SCSIZE nBlockCount = 0;
SCSIZE nStartIndex = 0, nEndIndex = 0;
for (i = 0; i < nCount; i++)
if ((pItems[i].nRow >= nRow1) && (pItems[i].nRow <= nRow2))
{
if (!nBlockCount)
nStartIndex = i;
nEndIndex = i;
++nBlockCount;
// im Clipboard muessen interpretierte Zellen stehen, um andere Formate
// (Text, Grafik...) erzueugen zu koennen
if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA )
{
ScFormulaCell* pFCell = (ScFormulaCell*) pItems[i].pCell;
if (pFCell->GetDirty() && pDocument->GetAutoCalc())
pFCell->Interpret();
}
}
if (nBlockCount)
{
int nCloneFlags = bCloneNoteCaptions ? SC_CLONECELL_DEFAULT : SC_CLONECELL_NOCAPTION;
rColumn.Resize( rColumn.GetCellCount() + nBlockCount );
ScAddress aOwnPos( nCol, 0, nTab );
ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab );
for (i = nStartIndex; i <= nEndIndex; i++)
{
aOwnPos.SetRow( pItems[i].nRow );
aDestPos.SetRow( pItems[i].nRow );
ScBaseCell* pNewCell = pItems[i].pCell->CloneWithNote( aOwnPos, *rColumn.pDocument, aDestPos, nCloneFlags );
rColumn.Append( aDestPos.Row(), pNewCell );
}
}
}
void ScColumn::CopyToColumn(SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, sal_Bool bMarked,
ScColumn& rColumn, const ScMarkData* pMarkData, sal_Bool bAsLink )
{
if (bMarked)
{
SCROW nStart, nEnd;
if (pMarkData && pMarkData->IsMultiMarked())
{
ScMarkArrayIter aIter( pMarkData->GetArray()+nCol );
while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 )
{
if ( nEnd >= nRow1 )
CopyToColumn( Max(nRow1,nStart), Min(nRow2,nEnd),
nFlags, sal_False, rColumn, pMarkData, bAsLink );
}
}
else
{
DBG_ERROR("CopyToColumn: bMarked, aber keine Markierung");
}
return;
}
if ( (nFlags & IDF_ATTRIB) != 0 )
{
if ( (nFlags & IDF_STYLES) != IDF_STYLES )
{ // StyleSheets im Zieldokument bleiben erhalten
// z.B. DIF und RTF Clipboard-Import
for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
{
const ScStyleSheet* pStyle =
rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet();
const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
ScPatternAttr* pNewPattern = new ScPatternAttr( *pPattern );
pNewPattern->SetStyleSheet( (ScStyleSheet*)pStyle );
rColumn.pAttrArray->SetPattern( nRow, pNewPattern, sal_True );
delete pNewPattern;
}
}
else
pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray);
}
if ((nFlags & IDF_CONTENTS) != 0)
{
SCSIZE i;
SCSIZE nBlockCount = 0;
SCSIZE nStartIndex = 0, nEndIndex = 0;
for (i = 0; i < nCount; i++)
if ((pItems[i].nRow >= nRow1) && (pItems[i].nRow <= nRow2))
{
if (!nBlockCount)
nStartIndex = i;
nEndIndex = i;
++nBlockCount;
}
if (nBlockCount)
{
rColumn.Resize( rColumn.GetCellCount() + nBlockCount );
ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab );
for (i = nStartIndex; i <= nEndIndex; i++)
{
aDestPos.SetRow( pItems[i].nRow );
ScBaseCell* pNew = bAsLink ?
CreateRefCell( rColumn.pDocument, aDestPos, i, nFlags ) :
CloneCell( i, nFlags, *rColumn.pDocument, aDestPos );
if (pNew)
rColumn.Insert(pItems[i].nRow, pNew);
}
}
}
}
void ScColumn::UndoToColumn(SCROW nRow1, SCROW nRow2, sal_uInt16 nFlags, sal_Bool bMarked,
ScColumn& rColumn, const ScMarkData* pMarkData )
{
if (nRow1 > 0)
CopyToColumn( 0, nRow1-1, IDF_FORMULA, sal_False, rColumn );
CopyToColumn( nRow1, nRow2, nFlags, bMarked, rColumn, pMarkData ); //! bMarked ????
if (nRow2 < MAXROW)
CopyToColumn( nRow2+1, MAXROW, IDF_FORMULA, sal_False, rColumn );
}
void ScColumn::CopyUpdated( const ScColumn& rPosCol, ScColumn& rDestCol ) const
{
ScDocument& rDestDoc = *rDestCol.pDocument;
ScAddress aOwnPos( nCol, 0, nTab );
ScAddress aDestPos( rDestCol.nCol, 0, rDestCol.nTab );
SCSIZE nPosCount = rPosCol.nCount;
for (SCSIZE nPosIndex = 0; nPosIndex < nPosCount; nPosIndex++)
{
aOwnPos.SetRow( rPosCol.pItems[nPosIndex].nRow );
aDestPos.SetRow( aOwnPos.Row() );
SCSIZE nThisIndex;
if ( Search( aDestPos.Row(), nThisIndex ) )
{
ScBaseCell* pNew = pItems[nThisIndex].pCell->CloneWithNote( aOwnPos, rDestDoc, aDestPos );
rDestCol.Insert( aDestPos.Row(), pNew );
}
}
// Dummy:
// CopyToColumn( 0,MAXROW, IDF_FORMULA, sal_False, rDestCol, NULL, sal_False );
}
void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
{
// Dies ist die Szenario-Tabelle, die Daten werden hineinkopiert
ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
SCROW nStart = -1, nEnd = -1;
const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
while (pPattern)
{
if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() )
{
DeleteArea( nStart, nEnd, IDF_CONTENTS );
((ScColumn&)rSrcCol).
CopyToColumn( nStart, nEnd, IDF_CONTENTS, sal_False, *this );
// UpdateUsed nicht noetig, schon in TestCopyScenario passiert
SCsTAB nDz = nTab - rSrcCol.nTab;
UpdateReference(URM_COPY, nCol, nStart, nTab,
nCol, nEnd, nTab,
0, 0, nDz, NULL);
UpdateCompile();
}
//! CopyToColumn "const" machen !!!
pPattern = aAttrIter.Next( nStart, nEnd );
}
}
void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
{
// Dies ist die Szenario-Tabelle, die Daten werden in die andere kopiert
ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
SCROW nStart = -1, nEnd = -1;
const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
while (pPattern)
{
if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() )
{
rDestCol.DeleteArea( nStart, nEnd, IDF_CONTENTS );
((ScColumn*)this)->
CopyToColumn( nStart, nEnd, IDF_CONTENTS, sal_False, rDestCol );
// UpdateUsed nicht noetig, schon in TestCopyScenario passiert
SCsTAB nDz = rDestCol.nTab - nTab;
rDestCol.UpdateReference(URM_COPY, rDestCol.nCol, nStart, rDestCol.nTab,
rDestCol.nCol, nEnd, rDestCol.nTab,
0, 0, nDz, NULL);
rDestCol.UpdateCompile();
}
//! CopyToColumn "const" machen !!!
pPattern = aAttrIter.Next( nStart, nEnd );
}
}
sal_Bool ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const
{
sal_Bool bOk = sal_True;
ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
SCROW nStart = 0, nEnd = 0;
const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
while (pPattern && bOk)
{
if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() )
if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HASATTR_PROTECTED ) )
bOk = sal_False;
pPattern = aAttrIter.Next( nStart, nEnd );
}
return bOk;
}
void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const
{
ScRange aRange( nCol, 0, nTab );
ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW );
SCROW nStart = -1, nEnd = -1;
const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
while (pPattern)
{
if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() )
{
aRange.aStart.SetRow( nStart );
aRange.aEnd.SetRow( nEnd );
rDestMark.SetMultiMarkArea( aRange, sal_True );
}
pPattern = aAttrIter.Next( nStart, nEnd );
}
}
void ScColumn::SwapCol(ScColumn& rCol)
{
SCSIZE nTemp;
nTemp = rCol.nCount;
rCol.nCount = nCount;
nCount = nTemp;
nTemp = rCol.nLimit;
rCol.nLimit = nLimit;
nLimit = nTemp;
ColEntry* pTempItems = rCol.pItems;
rCol.pItems = pItems;
pItems = pTempItems;
ScAttrArray* pTempAttr = rCol.pAttrArray;
rCol.pAttrArray = pAttrArray;
pAttrArray = pTempAttr;
// #38415# AttrArray muss richtige Spaltennummer haben
pAttrArray->SetCol(nCol);
rCol.pAttrArray->SetCol(rCol.nCol);
SCSIZE i;
if (pItems)
for (i = 0; i < nCount; i++)
{
ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
pCell->aPos.SetCol(nCol);
}
if (rCol.pItems)
for (i = 0; i < rCol.nCount; i++)
{
ScFormulaCell* pCell = (ScFormulaCell*) rCol.pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
pCell->aPos.SetCol(rCol.nCol);
}
}
void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol)
{
pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray);
if (pItems)
{
::std::vector<SCROW> aRows;
bool bConsecutive = true;
SCSIZE i;
Search( nStartRow, i); // i points to start row or position thereafter
SCSIZE nStartPos = i;
for ( ; i < nCount && pItems[i].nRow <= nEndRow; ++i)
{
SCROW nRow = pItems[i].nRow;
aRows.push_back( nRow);
rCol.Insert( nRow, pItems[i].pCell);
if (nRow != pItems[i].nRow)
{ // Listener inserted
bConsecutive = false;
Search( nRow, i);
}
}
SCSIZE nStopPos = i;
if (nStartPos < nStopPos)
{
// Create list of ranges of cell entry positions
typedef ::std::pair<SCSIZE,SCSIZE> PosPair;
typedef ::std::vector<PosPair> EntryPosPairs;
EntryPosPairs aEntries;
if (bConsecutive)
aEntries.push_back( PosPair(nStartPos, nStopPos));
else
{
bool bFirst = true;
nStopPos = 0;
for (::std::vector<SCROW>::const_iterator it( aRows.begin());
it != aRows.end() && nStopPos < nCount; ++it,
++nStopPos)
{
if (!bFirst && *it != pItems[nStopPos].nRow)
{
aEntries.push_back( PosPair(nStartPos, nStopPos));
bFirst = true;
}
if (bFirst && Search( *it, nStartPos))
{
bFirst = false;
nStopPos = nStartPos;
}
}
if (!bFirst && nStartPos < nStopPos)
aEntries.push_back( PosPair(nStartPos, nStopPos));
}
// Broadcast changes
ScAddress aAdr( nCol, 0, nTab );
ScHint aHint( SC_HINT_DYING, aAdr, NULL ); // areas only
ScAddress& rAddress = aHint.GetAddress();
ScNoteCell* pNoteCell = new ScNoteCell; // Dummy like in DeleteRange
// #121990# must iterate backwards, because indexes of following cells become invalid
for (EntryPosPairs::reverse_iterator it( aEntries.rbegin());
it != aEntries.rend(); ++it)
{
nStartPos = (*it).first;
nStopPos = (*it).second;
for (i=nStartPos; i<nStopPos; ++i)
pItems[i].pCell = pNoteCell;
for (i=nStartPos; i<nStopPos; ++i)
{
rAddress.SetRow( pItems[i].nRow );
pDocument->AreaBroadcast( aHint );
}
nCount -= nStopPos - nStartPos;
memmove( &pItems[nStartPos], &pItems[nStopPos],
(nCount - nStartPos) * sizeof(ColEntry) );
}
delete pNoteCell;
pItems[nCount].nRow = 0;
pItems[nCount].pCell = NULL;
}
}
}
void ScColumn::UpdateReference( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
SCCOL nCol2, SCROW nRow2, SCTAB nTab2, SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
ScDocument* pUndoDoc )
{
if (pItems)
{
ScRange aRange( ScAddress( nCol1, nRow1, nTab1 ),
ScAddress( nCol2, nRow2, nTab2 ) );
if ( eUpdateRefMode == URM_COPY && nRow1 == nRow2 )
{ // z.B. eine einzelne Zelle aus dem Clipboard eingefuegt
SCSIZE nIndex;
if ( Search( nRow1, nIndex ) )
{
ScFormulaCell* pCell = (ScFormulaCell*) pItems[nIndex].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
pCell->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc );
}
}
else
{
// #90279# For performance reasons two loop bodies instead of
// testing for update mode in each iteration.
// Anyways, this is still a bottleneck on large arrays with few
// formulas cells.
if ( eUpdateRefMode == URM_COPY )
{
SCSIZE i;
Search( nRow1, i );
for ( ; i < nCount; i++ )
{
SCROW nRow = pItems[i].nRow;
if ( nRow > nRow2 )
break;
ScBaseCell* pCell = pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
{
((ScFormulaCell*)pCell)->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener removed/inserted?
}
}
}
else
{
SCSIZE i = 0;
for ( ; i < nCount; i++ )
{
ScBaseCell* pCell = pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
{
SCROW nRow = pItems[i].nRow;
// When deleting rows on several sheets, the formula's position may be updated with the first call,
// so the undo position must be passed from here.
ScAddress aUndoPos( nCol, nRow, nTab );
((ScFormulaCell*)pCell)->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc, &aUndoPos );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener removed/inserted?
}
}
}
}
}
}
void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
ScDocument* pUndoDoc )
{
if (pItems)
for (SCSIZE i=0; i<nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if (pCell->GetCellType() == CELLTYPE_FORMULA)
{
SCROW nRow = pItems[i].nRow;
((ScFormulaCell*)pCell)->UpdateTranspose( rSource, rDest, pUndoDoc );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
{
if (pItems)
for (SCSIZE i=0; i<nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if (pCell->GetCellType() == CELLTYPE_FORMULA)
{
SCROW nRow = pItems[i].nRow;
((ScFormulaCell*)pCell)->UpdateGrow( rArea, nGrowX, nGrowY );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::UpdateInsertTab( SCTAB nTable)
{
if (nTab >= nTable)
pAttrArray->SetTab(++nTab);
if( pItems )
UpdateInsertTabOnlyCells( nTable );
}
void ScColumn::UpdateInsertTabOnlyCells( SCTAB nTable)
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
{
SCROW nRow = pItems[i].nRow;
pCell->UpdateInsertTab(nTable);
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::UpdateInsertTabAbs(SCTAB nTable)
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell;
if( pCell->GetCellType() == CELLTYPE_FORMULA)
{
SCROW nRow = pItems[i].nRow;
pCell->UpdateInsertTabAbs(nTable);
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::UpdateDeleteTab( SCTAB nTable, sal_Bool bIsMove, ScColumn* pRefUndo )
{
if (nTab > nTable)
pAttrArray->SetTab(--nTab);
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA )
{
SCROW nRow = pItems[i].nRow;
ScFormulaCell* pOld = (ScFormulaCell*)pItems[i].pCell;
/* Do not copy cell note to the undo document. Undo will copy
back the formula cell while keeping the original note. */
ScBaseCell* pSave = pRefUndo ? pOld->CloneWithoutNote( *pDocument ) : 0;
sal_Bool bChanged = pOld->UpdateDeleteTab(nTable, bIsMove);
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
if (pRefUndo)
{
if (bChanged)
pRefUndo->Insert( nRow, pSave );
else if(pSave)
pSave->Delete();
}
}
}
void ScColumn::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo )
{
nTab = nTabNo;
pAttrArray->SetTab( nTabNo );
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
{
SCROW nRow = pItems[i].nRow;
pCell->UpdateMoveTab( nOldPos, nNewPos, nTabNo );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::UpdateCompile( sal_Bool bForceIfNameInUse )
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
if( p->GetCellType() == CELLTYPE_FORMULA )
{
SCROW nRow = pItems[i].nRow;
p->UpdateCompile( bForceIfNameInUse );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::SetTabNo(SCTAB nNewTab)
{
nTab = nNewTab;
pAttrArray->SetTab( nNewTab );
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
if( p->GetCellType() == CELLTYPE_FORMULA )
p->aPos.SetTab( nNewTab );
}
}
sal_Bool ScColumn::IsRangeNameInUse(SCROW nRow1, SCROW nRow2, sal_uInt16 nIndex) const
{
sal_Bool bInUse = sal_False;
if (pItems)
for (SCSIZE i = 0; !bInUse && (i < nCount); i++)
if ((pItems[i].nRow >= nRow1) &&
(pItems[i].nRow <= nRow2) &&
(pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA))
bInUse = ((ScFormulaCell*)pItems[i].pCell)->IsRangeNameInUse(nIndex);
return bInUse;
}
void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, std::set<sal_uInt16>& rIndexes) const
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
if ((pItems[i].nRow >= nRow1) &&
(pItems[i].nRow <= nRow2) &&
(pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA))
((ScFormulaCell*)pItems[i].pCell)->FindRangeNamesInUse(rIndexes);
}
void ScColumn::ReplaceRangeNamesInUse(SCROW nRow1, SCROW nRow2,
const ScRangeData::IndexMap& rMap )
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
if ((pItems[i].nRow >= nRow1) &&
(pItems[i].nRow <= nRow2) &&
(pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA))
{
SCROW nRow = pItems[i].nRow;
((ScFormulaCell*)pItems[i].pCell)->ReplaceRangeNamesInUse( rMap );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::SetDirtyVar()
{
for (SCSIZE i=0; i<nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
if( p->GetCellType() == CELLTYPE_FORMULA )
p->SetDirtyVar();
}
}
void ScColumn::SetDirty()
{
// wird nur dokumentweit verwendet, kein FormulaTrack
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // Mehrfachberechnungen vermeiden
for (SCSIZE i=0; i<nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
if( p->GetCellType() == CELLTYPE_FORMULA )
{
p->SetDirtyVar();
if ( !pDocument->IsInFormulaTree( p ) )
pDocument->PutInFormulaTree( p );
}
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::SetDirty( const ScRange& rRange )
{ // broadcastet alles innerhalb eines Range, mit FormulaTrack
if ( !pItems || !nCount )
return ;
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // Mehrfachberechnungen vermeiden
SCROW nRow2 = rRange.aEnd.Row();
ScAddress aPos( nCol, 0, nTab );
ScHint aHint( SC_HINT_DATACHANGED, aPos, NULL );
SCROW nRow;
SCSIZE nIndex;
Search( rRange.aStart.Row(), nIndex );
while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*)pCell)->SetDirty();
else
{
aHint.GetAddress().SetRow( nRow );
aHint.SetCell( pCell );
pDocument->Broadcast( aHint );
}
nIndex++;
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::SetTableOpDirty( const ScRange& rRange )
{
if ( !pItems || !nCount )
return ;
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // no multiple recalculation
SCROW nRow2 = rRange.aEnd.Row();
ScAddress aPos( nCol, 0, nTab );
ScHint aHint( SC_HINT_TABLEOPDIRTY, aPos, NULL );
SCROW nRow;
SCSIZE nIndex;
Search( rRange.aStart.Row(), nIndex );
while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*)pCell)->SetTableOpDirty();
else
{
aHint.GetAddress().SetRow( nRow );
aHint.SetCell( pCell );
pDocument->Broadcast( aHint );
}
nIndex++;
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::SetDirtyAfterLoad()
{
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // Mehrfachberechnungen vermeiden
for (SCSIZE i=0; i<nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
#if 1
// Simply set dirty and append to FormulaTree, without broadcasting,
// which is a magnitude faster. This is used to calculate the entire
// document, e.g. when loading alien file formats.
if ( p->GetCellType() == CELLTYPE_FORMULA )
p->SetDirtyAfterLoad();
#else
/* This was used with the binary file format that stored results, where only
* newly compiled and volatile functions and their dependents had to be
* recalculated, which was faster then. Since that was moved to 'binfilter' to
* convert to an XML file this isn't needed anymore, and not used for other
* file formats. Kept for reference in case mechanism needs to be reactivated
* for some file formats, we'd have to introduce a controlling parameter to
* this method here then.
*/
// If the cell was alsready dirty because of CalcAfterLoad,
// FormulaTracking has to take place.
if ( p->GetCellType() == CELLTYPE_FORMULA && p->GetDirty() )
p->SetDirty();
#endif
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::SetRelNameDirty()
{
sal_Bool bOldAutoCalc = pDocument->GetAutoCalc();
pDocument->SetAutoCalc( sal_False ); // Mehrfachberechnungen vermeiden
for (SCSIZE i=0; i<nCount; i++)
{
ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell;
if( p->GetCellType() == CELLTYPE_FORMULA && p->HasRelNameReference() )
p->SetDirty();
}
pDocument->SetAutoCalc( bOldAutoCalc );
}
void ScColumn::CalcAll()
{
if (pItems)
for (SCSIZE i=0; i<nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if (pCell->GetCellType() == CELLTYPE_FORMULA)
{
#if OSL_DEBUG_LEVEL > 1
// nach F9 ctrl-F9: ueberprueft die Berechnung per FormulaTree
ScFormulaCell* pFCell = (ScFormulaCell*)pCell;
double nOldVal, nNewVal;
nOldVal = pFCell->GetValue();
#endif
((ScFormulaCell*)pCell)->Interpret();
#if OSL_DEBUG_LEVEL > 1
if ( pFCell->GetCode()->IsRecalcModeNormal() )
nNewVal = pFCell->GetValue();
else
nNewVal = nOldVal; // random(), jetzt() etc.
DBG_ASSERT( nOldVal==nNewVal, "CalcAll: nOldVal != nNewVal" );
#endif
}
}
}
void ScColumn::CompileAll()
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
{
SCROW nRow = pItems[i].nRow;
// fuer unbedingtes kompilieren
// bCompile=sal_True und pCode->nError=0
((ScFormulaCell*)pCell)->GetCode()->SetCodeError( 0 );
((ScFormulaCell*)pCell)->SetCompile( sal_True );
((ScFormulaCell*)pCell)->CompileTokenArray();
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::CompileXML( ScProgress& rProgress )
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
{
SCROW nRow = pItems[i].nRow;
((ScFormulaCell*)pCell)->CompileXML( rProgress );
if ( nRow != pItems[i].nRow )
Search( nRow, i ); // Listener geloescht/eingefuegt?
}
}
}
void ScColumn::CalcAfterLoad()
{
if (pItems)
for (SCSIZE i = 0; i < nCount; i++)
{
ScBaseCell* pCell = pItems[i].pCell;
if ( pCell->GetCellType() == CELLTYPE_FORMULA )
((ScFormulaCell*)pCell)->CalcAfterLoad();
}
}
void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow )
{
if (pItems)
{
SCSIZE nIndex;
Search(nStartRow,nIndex);
while (nIndex<nCount && pItems[nIndex].nRow <= nEndRow)
{
ScBaseCell* pCell = pItems[nIndex].pCell;
if (pCell->GetCellType() == CELLTYPE_FORMULA)
((ScFormulaCell*)pCell)->ResetChanged();
++nIndex;
}
}
}
sal_Bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst) const
{
// used in GetOptimalHeight - ambiguous script type counts as edit cell
SCROW nRow = 0;
SCSIZE nIndex;
Search(nStartRow,nIndex);
while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : sal_False )
{
ScBaseCell* pCell = pItems[nIndex].pCell;
CellType eCellType = pCell->GetCellType();
if ( eCellType == CELLTYPE_EDIT ||
IsAmbiguousScriptNonZero( pDocument->GetScriptType(nCol, nRow, nTab, pCell) ) ||
((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) )
{
rFirst = nRow;
return sal_True;
}
++nIndex;
}
return sal_False;
}
SCsROW ScColumn::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle,
sal_Bool bUp, sal_Bool bInSelection, const ScMarkData& rMark )
{
if (bInSelection)
{
if (rMark.IsMultiMarked())
return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp,
(ScMarkArray*) rMark.GetArray()+nCol ); //! const
else
return -1;
}
else
return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp, NULL );
}
sal_Bool ScColumn::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow, const ScStyleSheet* pSearchStyle,
sal_Bool bUp, sal_Bool bInSelection, const ScMarkData& rMark )
{
if (bInSelection)
{
if (rMark.IsMultiMarked())
return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp,
(ScMarkArray*) rMark.GetArray()+nCol ); //! const
else
return sal_False;
}
else
return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp, NULL );
}