blob: 0bc51c7c994e57c34b681c3859501ff50d0f2356 [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"
//------------------------------------------------------------------------
#define SC_RANGELST_CXX //fuer ICC
#include <tools/debug.hxx>
#include <stdlib.h> // qsort
#include <unotools/collatorwrapper.hxx>
#include "rangelst.hxx"
#include "document.hxx"
#include "refupdat.hxx"
#include "rechead.hxx"
#include "compiler.hxx"
// === ScRangeList ====================================================
ScRangeList::~ScRangeList()
{
for ( ScRangePtr pR = First(); pR; pR = Next() )
delete pR;
}
void ScRangeList::RemoveAll()
{
for ( ScRangePtr pR = First(); pR; pR = Next() )
delete pR;
Clear();
}
sal_uInt16 ScRangeList::Parse( const String& rStr, ScDocument* pDoc, sal_uInt16 nMask,
formula::FormulaGrammar::AddressConvention eConv,
sal_Unicode cDelimiter )
{
if ( rStr.Len() )
{
if (!cDelimiter)
cDelimiter = ScCompiler::GetNativeSymbol(ocSep).GetChar(0);
nMask |= SCA_VALID; // falls das jemand vergessen sollte
sal_uInt16 nResult = (sal_uInt16)~0; // alle Bits setzen
ScRange aRange;
String aOne;
SCTAB nTab = 0;
if ( pDoc )
{
//! erste markierte Tabelle gibts nicht mehr am Dokument
//! -> uebergeben? oder spaeter an den Ranges setzen
}
else
nTab = 0;
sal_uInt16 nTCount = rStr.GetTokenCount( cDelimiter );
for ( sal_uInt16 i=0; i<nTCount; i++ )
{
aOne = rStr.GetToken( i, cDelimiter );
// FIXME : broken for Lotus
if ( aOne.Search( ':' ) == STRING_NOTFOUND )
{ // Range muss es sein
String aStrTmp( aOne );
aOne += ':';
aOne += aStrTmp;
}
aRange.aStart.SetTab( nTab ); // Default Tab wenn nicht angegeben
sal_uInt16 nRes = aRange.Parse( aOne, pDoc, eConv );
if ( (nRes & nMask) == nMask )
Append( aRange );
nResult &= nRes; // alle gemeinsamen Bits bleiben erhalten
}
return nResult; // SCA_VALID gesetzt wenn alle ok
}
else
return 0;
}
void ScRangeList::Format( String& rStr, sal_uInt16 nFlags, ScDocument* pDoc,
formula::FormulaGrammar::AddressConvention eConv,
sal_Unicode cDelimiter ) const
{
rStr.Erase();
if (!cDelimiter)
cDelimiter = ScCompiler::GetNativeSymbol(ocSep).GetChar(0);
sal_uLong nCnt = Count();
for ( sal_uLong nIdx = 0; nIdx < nCnt; nIdx++ )
{
String aStr;
GetObject( nIdx )->Format( aStr, nFlags, pDoc, eConv );
if ( nIdx )
rStr += cDelimiter;
rStr += aStr;
}
}
void ScRangeList::Join( const ScRange& r, sal_Bool bIsInList )
{
if ( !Count() )
{
Append( r );
return ;
}
SCCOL nCol1 = r.aStart.Col();
SCROW nRow1 = r.aStart.Row();
SCTAB nTab1 = r.aStart.Tab();
SCCOL nCol2 = r.aEnd.Col();
SCROW nRow2 = r.aEnd.Row();
SCTAB nTab2 = r.aEnd.Tab();
ScRangePtr pOver = (ScRangePtr) &r; // fies aber wahr wenn bInList
sal_uLong nOldPos = 0;
if ( bIsInList )
{ // merken um ggbf. zu loeschen bzw. wiederherzustellen
nOldPos = GetPos( pOver );
}
sal_Bool bJoinedInput = sal_False;
for ( ScRangePtr p = First(); p && pOver; p = Next() )
{
if ( p == pOver )
continue; // derselbe, weiter mit dem naechsten
sal_Bool bJoined = sal_False;
if ( p->In( r ) )
{ // Range r in Range p enthalten oder identisch
if ( bIsInList )
bJoined = sal_True; // weg mit Range r
else
{ // das war's dann
bJoinedInput = sal_True; // nicht anhaengen
break; // for
}
}
else if ( r.In( *p ) )
{ // Range p in Range r enthalten, r zum neuen Range machen
*p = r;
bJoined = sal_True;
}
if ( !bJoined && p->aStart.Tab() == nTab1 && p->aEnd.Tab() == nTab2 )
{ // 2D
if ( p->aStart.Col() == nCol1 && p->aEnd.Col() == nCol2 )
{
if ( p->aStart.Row() == nRow2+1 )
{ // oben
p->aStart.SetRow( nRow1 );
bJoined = sal_True;
}
else if ( p->aEnd.Row() == nRow1-1 )
{ // unten
p->aEnd.SetRow( nRow2 );
bJoined = sal_True;
}
}
else if ( p->aStart.Row() == nRow1 && p->aEnd.Row() == nRow2 )
{
if ( p->aStart.Col() == nCol2+1 )
{ // links
p->aStart.SetCol( nCol1 );
bJoined = sal_True;
}
else if ( p->aEnd.Col() == nCol1-1 )
{ // rechts
p->aEnd.SetCol( nCol2 );
bJoined = sal_True;
}
}
}
if ( bJoined )
{
if ( bIsInList )
{ // innerhalb der Liste Range loeschen
Remove( nOldPos );
delete pOver;
pOver = NULL;
if ( nOldPos )
nOldPos--; // Seek richtig aufsetzen
}
bJoinedInput = sal_True;
Join( *p, sal_True ); // rekursiv!
}
}
if ( bIsInList )
Seek( nOldPos );
else if ( !bJoinedInput )
Append( r );
}
sal_Bool ScRangeList::operator==( const ScRangeList& r ) const
{
if ( this == &r )
return sal_True; // identische Referenz
if ( Count() != r.Count() )
return sal_False;
sal_uLong nCnt = Count();
for ( sal_uLong nIdx = 0; nIdx < nCnt; nIdx++ )
{
if ( *GetObject( nIdx ) != *r.GetObject( nIdx ) )
return sal_False; // auch andere Reihenfolge ist ungleich
}
return sal_True;
}
sal_Bool ScRangeList::operator!=( const ScRangeList& r ) const
{
return !operator==( r );
}
sal_Bool ScRangeList::UpdateReference( UpdateRefMode eUpdateRefMode,
ScDocument* pDoc, const ScRange& rWhere,
SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
sal_Bool bChanged = sal_False;
if ( Count() )
{
SCCOL nCol1;
SCROW nRow1;
SCTAB nTab1;
SCCOL nCol2;
SCROW nRow2;
SCTAB nTab2;
rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
for ( ScRange* pR = First(); pR; pR = Next() )
{
SCCOL theCol1;
SCROW theRow1;
SCTAB theTab1;
SCCOL theCol2;
SCROW theRow2;
SCTAB theTab2;
pR->GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
nDx, nDy, nDz,
theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
!= UR_NOTHING )
{
bChanged = sal_True;
pR->aStart.Set( theCol1, theRow1, theTab1 );
pR->aEnd.Set( theCol2, theRow2, theTab2 );
}
}
}
return bChanged;
}
ScRange* ScRangeList::Find( const ScAddress& rAdr ) const
{
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
{
ScRange* pR = GetObject( j );
if ( pR->In( rAdr ) )
return pR;
}
return NULL;
}
ScRangeList::ScRangeList( const ScRangeList& rList ) :
ScRangeListBase(),
SvRefBase()
{
sal_uLong nListCount = rList.Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
Append( *rList.GetObject( j ) );
}
ScRangeList& ScRangeList::operator=(const ScRangeList& rList)
{
RemoveAll();
sal_uLong nListCount = rList.Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
Append( *rList.GetObject( j ) );
return *this;
}
sal_Bool ScRangeList::Intersects( const ScRange& rRange ) const
{
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
if ( GetObject(j)->Intersects( rRange ) )
return sal_True;
return sal_False;
}
sal_Bool ScRangeList::In( const ScRange& rRange ) const
{
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
if ( GetObject(j)->In( rRange ) )
return sal_True;
return sal_False;
}
sal_uLong ScRangeList::GetCellCount() const
{
sal_uLong nCellCount = 0;
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
{
ScRange* pR = GetObject( j );
nCellCount += sal_uLong(pR->aEnd.Col() - pR->aStart.Col() + 1)
* sal_uLong(pR->aEnd.Row() - pR->aStart.Row() + 1)
* sal_uLong(pR->aEnd.Tab() - pR->aStart.Tab() + 1);
}
return nCellCount;
}
// === ScRangePairList ====================================================
ScRangePairList::~ScRangePairList()
{
for ( ScRangePair* pR = First(); pR; pR = Next() )
delete pR;
}
void ScRangePairList::Join( const ScRangePair& r, sal_Bool bIsInList )
{
if ( !Count() )
{
Append( r );
return ;
}
const ScRange& r1 = r.GetRange(0);
const ScRange& r2 = r.GetRange(1);
SCCOL nCol1 = r1.aStart.Col();
SCROW nRow1 = r1.aStart.Row();
SCTAB nTab1 = r1.aStart.Tab();
SCCOL nCol2 = r1.aEnd.Col();
SCROW nRow2 = r1.aEnd.Row();
SCTAB nTab2 = r1.aEnd.Tab();
ScRangePair* pOver = (ScRangePair*) &r; // fies aber wahr wenn bInList
sal_uLong nOldPos = 0;
if ( bIsInList )
{ // merken um ggbf. zu loeschen bzw. wiederherzustellen
nOldPos = GetPos( pOver );
}
sal_Bool bJoinedInput = sal_False;
for ( ScRangePair* p = First(); p && pOver; p = Next() )
{
if ( p == pOver )
continue; // derselbe, weiter mit dem naechsten
sal_Bool bJoined = sal_False;
ScRange& rp1 = p->GetRange(0);
ScRange& rp2 = p->GetRange(1);
if ( rp2 == r2 )
{ // nur wenn Range2 gleich ist
if ( rp1.In( r1 ) )
{ // RangePair r in RangePair p enthalten oder identisch
if ( bIsInList )
bJoined = sal_True; // weg mit RangePair r
else
{ // das war's dann
bJoinedInput = sal_True; // nicht anhaengen
break; // for
}
}
else if ( r1.In( rp1 ) )
{ // RangePair p in RangePair r enthalten, r zum neuen RangePair machen
*p = r;
bJoined = sal_True;
}
}
if ( !bJoined && rp1.aStart.Tab() == nTab1 && rp1.aEnd.Tab() == nTab2
&& rp2.aStart.Tab() == r2.aStart.Tab()
&& rp2.aEnd.Tab() == r2.aEnd.Tab() )
{ // 2D, Range2 muss genauso nebeneinander liegen wie Range1
if ( rp1.aStart.Col() == nCol1 && rp1.aEnd.Col() == nCol2
&& rp2.aStart.Col() == r2.aStart.Col()
&& rp2.aEnd.Col() == r2.aEnd.Col() )
{
if ( rp1.aStart.Row() == nRow2+1
&& rp2.aStart.Row() == r2.aEnd.Row()+1 )
{ // oben
rp1.aStart.SetRow( nRow1 );
rp2.aStart.SetRow( r2.aStart.Row() );
bJoined = sal_True;
}
else if ( rp1.aEnd.Row() == nRow1-1
&& rp2.aEnd.Row() == r2.aStart.Row()-1 )
{ // unten
rp1.aEnd.SetRow( nRow2 );
rp2.aEnd.SetRow( r2.aEnd.Row() );
bJoined = sal_True;
}
}
else if ( rp1.aStart.Row() == nRow1 && rp1.aEnd.Row() == nRow2
&& rp2.aStart.Row() == r2.aStart.Row()
&& rp2.aEnd.Row() == r2.aEnd.Row() )
{
if ( rp1.aStart.Col() == nCol2+1
&& rp2.aStart.Col() == r2.aEnd.Col()+1 )
{ // links
rp1.aStart.SetCol( nCol1 );
rp2.aStart.SetCol( r2.aStart.Col() );
bJoined = sal_True;
}
else if ( rp1.aEnd.Col() == nCol1-1
&& rp2.aEnd.Col() == r2.aEnd.Col()-1 )
{ // rechts
rp1.aEnd.SetCol( nCol2 );
rp2.aEnd.SetCol( r2.aEnd.Col() );
bJoined = sal_True;
}
}
}
if ( bJoined )
{
if ( bIsInList )
{ // innerhalb der Liste RangePair loeschen
Remove( nOldPos );
delete pOver;
pOver = NULL;
if ( nOldPos )
nOldPos--; // Seek richtig aufsetzen
}
bJoinedInput = sal_True;
Join( *p, sal_True ); // rekursiv!
}
}
if ( bIsInList )
Seek( nOldPos );
else if ( !bJoinedInput )
Append( r );
}
sal_Bool ScRangePairList::operator==( const ScRangePairList& r ) const
{
if ( this == &r )
return sal_True; // identische Referenz
if ( Count() != r.Count() )
return sal_False;
sal_uLong nCnt = Count();
for ( sal_uLong nIdx = 0; nIdx < nCnt; nIdx++ )
{
if ( *GetObject( nIdx ) != *r.GetObject( nIdx ) )
return sal_False; // auch andere Reihenfolge ist ungleich
}
return sal_True;
}
sal_Bool ScRangePairList::UpdateReference( UpdateRefMode eUpdateRefMode,
ScDocument* pDoc, const ScRange& rWhere,
SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
sal_Bool bChanged = sal_False;
if ( Count() )
{
SCCOL nCol1;
SCROW nRow1;
SCTAB nTab1;
SCCOL nCol2;
SCROW nRow2;
SCTAB nTab2;
rWhere.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
for ( ScRangePair* pR = First(); pR; pR = Next() )
{
for ( sal_uInt16 j=0; j<2; j++ )
{
ScRange& rRange = pR->GetRange(j);
SCCOL theCol1;
SCROW theRow1;
SCTAB theTab1;
SCCOL theCol2;
SCROW theRow2;
SCTAB theTab2;
rRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 );
if ( ScRefUpdate::Update( pDoc, eUpdateRefMode,
nCol1, nRow1, nTab1, nCol2, nRow2, nTab2,
nDx, nDy, nDz,
theCol1, theRow1, theTab1, theCol2, theRow2, theTab2 )
!= UR_NOTHING )
{
bChanged = sal_True;
rRange.aStart.Set( theCol1, theRow1, theTab1 );
rRange.aEnd.Set( theCol2, theRow2, theTab2 );
}
}
}
}
return bChanged;
}
void ScRangePairList::DeleteOnTab( SCTAB nTab )
{
// Delete entries that have the labels (first range) on nTab
sal_uLong nListCount = Count();
sal_uLong nPos = 0;
while ( nPos < nListCount )
{
ScRangePair* pR = GetObject( nPos );
ScRange aRange = pR->GetRange(0);
if ( aRange.aStart.Tab() == nTab && aRange.aEnd.Tab() == nTab )
{
Remove( nPos );
delete pR;
nListCount = Count();
}
else
++nPos;
}
}
ScRangePair* ScRangePairList::Find( const ScAddress& rAdr ) const
{
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
{
ScRangePair* pR = GetObject( j );
if ( pR->GetRange(0).In( rAdr ) )
return pR;
}
return NULL;
}
ScRangePair* ScRangePairList::Find( const ScRange& rRange ) const
{
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
{
ScRangePair* pR = GetObject( j );
if ( pR->GetRange(0) == rRange )
return pR;
}
return NULL;
}
ScRangePairList* ScRangePairList::Clone() const
{
ScRangePairList* pNew = new ScRangePairList;
sal_uLong nListCount = Count();
for ( sal_uLong j = 0; j < nListCount; j++ )
{
pNew->Append( *GetObject( j ) );
}
return pNew;
}
struct ScRangePairNameSort
{
ScRangePair* pPair;
ScDocument* pDoc;
};
extern "C" int
#ifdef WNT
__cdecl
#endif
ScRangePairList_QsortNameCompare( const void* p1, const void* p2 )
{
const ScRangePairNameSort* ps1 = (const ScRangePairNameSort*)p1;
const ScRangePairNameSort* ps2 = (const ScRangePairNameSort*)p2;
const ScAddress& rStartPos1 = ps1->pPair->GetRange(0).aStart;
const ScAddress& rStartPos2 = ps2->pPair->GetRange(0).aStart;
String aStr1, aStr2;
sal_Int32 nComp;
if ( rStartPos1.Tab() == rStartPos2.Tab() )
nComp = COMPARE_EQUAL;
else
{
ps1->pDoc->GetName( rStartPos1.Tab(), aStr1 );
ps2->pDoc->GetName( rStartPos2.Tab(), aStr2 );
nComp = ScGlobal::GetCollator()->compareString( aStr1, aStr2 );
}
switch ( nComp )
{
case COMPARE_LESS:
return -1;
//break;
case COMPARE_GREATER:
return 1;
//break;
default:
// gleiche Tabs
if ( rStartPos1.Col() < rStartPos2.Col() )
return -1;
if ( rStartPos1.Col() > rStartPos2.Col() )
return 1;
// gleiche Cols
if ( rStartPos1.Row() < rStartPos2.Row() )
return -1;
if ( rStartPos1.Row() > rStartPos2.Row() )
return 1;
// erste Ecke gleich, zweite Ecke
{
const ScAddress& rEndPos1 = ps1->pPair->GetRange(0).aEnd;
const ScAddress& rEndPos2 = ps2->pPair->GetRange(0).aEnd;
if ( rEndPos1.Tab() == rEndPos2.Tab() )
nComp = COMPARE_EQUAL;
else
{
ps1->pDoc->GetName( rEndPos1.Tab(), aStr1 );
ps2->pDoc->GetName( rEndPos2.Tab(), aStr2 );
nComp = ScGlobal::GetCollator()->compareString( aStr1, aStr2 );
}
switch ( nComp )
{
case COMPARE_LESS:
return -1;
//break;
case COMPARE_GREATER:
return 1;
//break;
default:
// gleiche Tabs
if ( rEndPos1.Col() < rEndPos2.Col() )
return -1;
if ( rEndPos1.Col() > rEndPos2.Col() )
return 1;
// gleiche Cols
if ( rEndPos1.Row() < rEndPos2.Row() )
return -1;
if ( rEndPos1.Row() > rEndPos2.Row() )
return 1;
return 0;
}
}
return 0;
}
return 0; // just in case
}
ScRangePair** ScRangePairList::CreateNameSortedArray( sal_uLong& nListCount,
ScDocument* pDoc ) const
{
nListCount = Count();
DBG_ASSERT( nListCount * sizeof(ScRangePairNameSort) <= (size_t)~0x1F,
"ScRangePairList::CreateNameSortedArray nListCount * sizeof(ScRangePairNameSort) > (size_t)~0x1F" );
ScRangePairNameSort* pSortArray = (ScRangePairNameSort*)
new sal_uInt8 [ nListCount * sizeof(ScRangePairNameSort) ];
sal_uLong j;
for ( j=0; j < nListCount; j++ )
{
pSortArray[j].pPair = GetObject( j );
pSortArray[j].pDoc = pDoc;
}
#if !(defined(ICC ) && defined(OS2))
qsort( (void*)pSortArray, nListCount, sizeof(ScRangePairNameSort), &ScRangePairList_QsortNameCompare );
#else
qsort( (void*)pSortArray, nListCount, sizeof(ScRangePairNameSort), ICCQsortRPairCompare );
#endif
// ScRangePair Pointer aufruecken
ScRangePair** ppSortArray = (ScRangePair**)pSortArray;
for ( j=0; j < nListCount; j++ )
{
ppSortArray[j] = pSortArray[j].pPair;
}
return ppSortArray;
}