blob: a180f0b2d98faff999ae961229817fe2028e18f0 [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 <tools/debug.hxx>
#include "markdata.hxx"
#include "markarr.hxx"
#include "rangelst.hxx"
// STATIC DATA -----------------------------------------------------------
//------------------------------------------------------------------------
ScMarkData::ScMarkData() :
pMultiSel( NULL )
{
for (SCTAB i=0; i<=MAXTAB; i++)
bTabMarked[i] = sal_False;
ResetMark();
}
ScMarkData::ScMarkData(const ScMarkData& rData) :
aMarkRange( rData.aMarkRange ),
aMultiRange( rData.aMultiRange ),
pMultiSel( NULL )
{
bMarked = rData.bMarked;
bMultiMarked = rData.bMultiMarked;
bMarking = rData.bMarking;
bMarkIsNeg = rData.bMarkIsNeg;
for (SCTAB i=0; i<=MAXTAB; i++)
bTabMarked[i] = rData.bTabMarked[i];
if (rData.pMultiSel)
{
pMultiSel = new ScMarkArray[MAXCOLCOUNT];
for (SCCOL j=0; j<MAXCOLCOUNT; j++)
rData.pMultiSel[j].CopyMarksTo( pMultiSel[j] );
}
}
ScMarkData& ScMarkData::operator=(const ScMarkData& rData)
{
if ( &rData == this )
return *this;
delete[] pMultiSel;
pMultiSel = NULL;
aMarkRange = rData.aMarkRange;
aMultiRange = rData.aMultiRange;
bMarked = rData.bMarked;
bMultiMarked = rData.bMultiMarked;
bMarking = rData.bMarking;
bMarkIsNeg = rData.bMarkIsNeg;
for (SCTAB i=0; i<=MAXTAB; i++)
bTabMarked[i] = rData.bTabMarked[i];
if (rData.pMultiSel)
{
pMultiSel = new ScMarkArray[MAXCOLCOUNT];
for (SCCOL j=0; j<MAXCOLCOUNT; j++)
rData.pMultiSel[j].CopyMarksTo( pMultiSel[j] );
}
return *this;
}
ScMarkData::~ScMarkData()
{
delete[] pMultiSel;
}
void ScMarkData::ResetMark()
{
delete[] pMultiSel;
pMultiSel = NULL;
bMarked = bMultiMarked = sal_False;
bMarking = bMarkIsNeg = sal_False;
}
void ScMarkData::SetMarkArea( const ScRange& rRange )
{
aMarkRange = rRange;
aMarkRange.Justify();
if ( !bMarked )
{
// #77987# Upon creation of a document ScFormatShell GetTextAttrState
// may query (default) attributes although no sheet is marked yet.
// => mark that one.
if ( !GetSelectCount() )
bTabMarked[ aMarkRange.aStart.Tab() ] = sal_True;
bMarked = sal_True;
}
}
void ScMarkData::GetMarkArea( ScRange& rRange ) const
{
rRange = aMarkRange; //! inline ?
}
void ScMarkData::GetMultiMarkArea( ScRange& rRange ) const
{
rRange = aMultiRange;
}
void ScMarkData::SetMultiMarkArea( const ScRange& rRange, sal_Bool bMark )
{
if (!pMultiSel)
{
pMultiSel = new ScMarkArray[MAXCOL+1];
// if simple mark range is set, copy to multi marks
if ( bMarked && !bMarkIsNeg )
{
bMarked = sal_False;
SetMultiMarkArea( aMarkRange, sal_True );
}
}
SCCOL nStartCol = rRange.aStart.Col();
SCROW nStartRow = rRange.aStart.Row();
SCCOL nEndCol = rRange.aEnd.Col();
SCROW nEndRow = rRange.aEnd.Row();
PutInOrder( nStartRow, nEndRow );
PutInOrder( nStartCol, nEndCol );
SCCOL nCol;
for (nCol=nStartCol; nCol<=nEndCol; nCol++)
pMultiSel[nCol].SetMarkArea( nStartRow, nEndRow, bMark );
if ( bMultiMarked ) // aMultiRange updaten
{
if ( nStartCol < aMultiRange.aStart.Col() )
aMultiRange.aStart.SetCol( nStartCol );
if ( nStartRow < aMultiRange.aStart.Row() )
aMultiRange.aStart.SetRow( nStartRow );
if ( nEndCol > aMultiRange.aEnd.Col() )
aMultiRange.aEnd.SetCol( nEndCol );
if ( nEndRow > aMultiRange.aEnd.Row() )
aMultiRange.aEnd.SetRow( nEndRow );
}
else
{
aMultiRange = rRange; // neu
bMultiMarked = sal_True;
}
}
void ScMarkData::SetAreaTab( SCTAB nTab )
{
aMarkRange.aStart.SetTab(nTab);
aMarkRange.aEnd.SetTab(nTab);
aMultiRange.aStart.SetTab(nTab);
aMultiRange.aEnd.SetTab(nTab);
}
void ScMarkData::SelectOneTable( SCTAB nTab )
{
for (SCTAB i=0; i<=MAXTAB; i++)
bTabMarked[i] = ( nTab == i );
}
SCTAB ScMarkData::GetSelectCount() const
{
SCTAB nCount = 0;
for (SCTAB i=0; i<=MAXTAB; i++)
if (bTabMarked[i])
++nCount;
return nCount;
}
SCTAB ScMarkData::GetFirstSelected() const
{
for (SCTAB i=0; i<=MAXTAB; i++)
if (bTabMarked[i])
return i;
DBG_ERROR("GetFirstSelected: keine markiert");
return 0;
}
void ScMarkData::MarkToMulti()
{
if ( bMarked && !bMarking )
{
SetMultiMarkArea( aMarkRange, !bMarkIsNeg );
bMarked = sal_False;
// check if all multi mark ranges have been removed
if ( bMarkIsNeg && !HasAnyMultiMarks() )
ResetMark();
}
}
void ScMarkData::MarkToSimple()
{
if ( bMarking )
return;
if ( bMultiMarked && bMarked )
MarkToMulti(); // may result in bMarked and bMultiMarked reset
if ( bMultiMarked )
{
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
ScRange aNew = aMultiRange;
sal_Bool bOk = sal_False;
SCCOL nStartCol = aNew.aStart.Col();
SCCOL nEndCol = aNew.aEnd.Col();
while ( nStartCol < nEndCol && !pMultiSel[nStartCol].HasMarks() )
++nStartCol;
while ( nStartCol < nEndCol && !pMultiSel[nEndCol].HasMarks() )
--nEndCol;
// Zeilen werden nur aus MarkArray genommen
SCROW nStartRow, nEndRow;
if ( pMultiSel[nStartCol].HasOneMark( nStartRow, nEndRow ) )
{
bOk = sal_True;
SCROW nCmpStart, nCmpEnd;
for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++)
if ( !pMultiSel[nCol].HasOneMark( nCmpStart, nCmpEnd )
|| nCmpStart != nStartRow || nCmpEnd != nEndRow )
bOk = sal_False;
}
if (bOk)
{
aNew.aStart.SetCol(nStartCol);
aNew.aStart.SetRow(nStartRow);
aNew.aEnd.SetCol(nEndCol);
aNew.aEnd.SetRow(nEndRow);
ResetMark();
aMarkRange = aNew;
bMarked = sal_True;
bMarkIsNeg = sal_False;
}
}
}
sal_Bool ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, sal_Bool bNoSimple ) const
{
if ( bMarked && !bNoSimple && !bMarkIsNeg )
if ( aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
return sal_True;
if (bMultiMarked)
{
//! hier auf negative Markierung testen ?
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
return pMultiSel[nCol].GetMark( nRow );
}
return sal_False;
}
sal_Bool ScMarkData::IsColumnMarked( SCCOL nCol ) const
{
// bMarkIsNeg inzwischen auch fuer Spaltenkoepfe
//! GetMarkColumnRanges fuer komplett markierte Spalten
if ( bMarked && !bMarkIsNeg &&
aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == MAXROW )
return sal_True;
if ( bMultiMarked && pMultiSel[nCol].IsAllMarked(0,MAXROW) )
return sal_True;
return sal_False;
}
sal_Bool ScMarkData::IsRowMarked( SCROW nRow ) const
{
// bMarkIsNeg inzwischen auch fuer Zeilenkoepfe
//! GetMarkRowRanges fuer komplett markierte Zeilen
if ( bMarked && !bMarkIsNeg &&
aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == MAXCOL &&
aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
return sal_True;
if ( bMultiMarked )
{
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
for (SCCOL nCol=0; nCol<=MAXCOL; nCol++)
if (!pMultiSel[nCol].GetMark(nRow))
return sal_False;
return sal_True;
}
return sal_False;
}
void ScMarkData::MarkFromRangeList( const ScRangeList& rList, sal_Bool bReset )
{
if (bReset)
{
for (SCTAB i=0; i<=MAXTAB; i++)
bTabMarked[i] = sal_False; // Tabellen sind nicht in ResetMark
ResetMark();
}
sal_uLong nCount = rList.Count();
if ( nCount == 1 && !bMarked && !bMultiMarked )
{
ScRange aRange = *rList.GetObject(0);
SetMarkArea( aRange );
SelectTable( aRange.aStart.Tab(), sal_True );
}
else
{
for (sal_uLong i=0; i<nCount; i++)
{
ScRange aRange = *rList.GetObject(i);
SetMultiMarkArea( aRange, sal_True );
SelectTable( aRange.aStart.Tab(), sal_True );
}
}
}
void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, sal_Bool bClear ) const
{
if (!pList)
return;
if (bClear)
pList->RemoveAll();
//! bei mehreren selektierten Tabellen mehrere Ranges eintragen !!!
if ( bMultiMarked )
{
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
SCTAB nTab = aMultiRange.aStart.Tab();
SCCOL nStartCol = aMultiRange.aStart.Col();
SCCOL nEndCol = aMultiRange.aEnd.Col();
for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
if (pMultiSel[nCol].HasMarks())
{
SCROW nTop, nBottom;
ScRange aRange( nCol, 0, nTab );
ScMarkArrayIter aMarkIter( &pMultiSel[nCol] );
while ( aMarkIter.Next( nTop, nBottom ) )
{
aRange.aStart.SetRow( nTop );
aRange.aEnd.SetRow( nBottom );
pList->Join( aRange );
}
}
}
if ( bMarked )
pList->Append( aMarkRange );
}
void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const
{
if (!pList)
return;
ScRangeList aOldList(*pList);
pList->RemoveAll(); //! oder die vorhandenen unten weglassen
for (SCTAB nTab=0; nTab<=MAXTAB; nTab++)
if (bTabMarked[nTab])
{
sal_uLong nCount = aOldList.Count();
for (sal_uLong i=0; i<nCount; i++)
{
ScRange aRange = *aOldList.GetObject(i);
aRange.aStart.SetTab(nTab);
aRange.aEnd.SetTab(nTab);
pList->Append( aRange );
}
}
}
SCCOLROW ScMarkData::GetMarkColumnRanges( SCCOLROW* pRanges )
{
if (bMarked)
MarkToMulti();
if (!bMultiMarked)
return 0;
DBG_ASSERT(pMultiSel, "bMultiMarked, but pMultiSel == 0");
const SCCOLROW nMultiStart = aMultiRange.aStart.Col();
const SCCOLROW nMultiEnd = aMultiRange.aEnd.Col();
if (nMultiStart == 0 && nMultiEnd == MAXCOL)
{
// One or more entire rows.
pRanges[0] = 0;
pRanges[1] = MAXCOL;
return 1;
}
SCCOLROW nRangeCnt = 0;
SCCOLROW nStart = nMultiStart;
while (nStart <= nMultiEnd)
{
while (nStart < nMultiEnd && !pMultiSel[nStart].HasMarks())
++nStart;
if (pMultiSel[nStart].HasMarks())
{
SCCOLROW nEnd = nStart;
while (nEnd < nMultiEnd && pMultiSel[nEnd].HasMarks())
++nEnd;
if (!pMultiSel[nEnd].HasMarks())
--nEnd;
pRanges[2*nRangeCnt ] = nStart;
pRanges[2*nRangeCnt+1] = nEnd;
++nRangeCnt;
nStart = nEnd+1;
}
else
nStart = nMultiEnd+1;
}
return nRangeCnt;
}
SCCOLROW ScMarkData::GetMarkRowRanges( SCCOLROW* pRanges )
{
if (bMarked)
MarkToMulti();
if (!bMultiMarked)
return 0;
DBG_ASSERT(pMultiSel, "bMultiMarked, but pMultiSel == 0");
// Which rows are marked?
// Optimized to not loop over MAXCOL*MAXROW as worst case, i.e. Ctrl+A
const SCCOLROW nMultiStart = aMultiRange.aStart.Row();
const SCCOLROW nMultiEnd = aMultiRange.aEnd.Row();
sal_Bool* bRowMarked = new sal_Bool[MAXROWCOUNT];
memset( bRowMarked, 0, sizeof(sal_Bool) * MAXROWCOUNT);
SCROW nRow;
SCCOL nCol;
SCROW nTop = -1, nBottom = -1;
for (nCol = aMultiRange.aStart.Col(); nCol <= aMultiRange.aEnd.Col(); ++nCol)
{
ScMarkArrayIter aMarkIter( &pMultiSel[nCol] );
while (aMarkIter.Next( nTop, nBottom ))
for (nRow=nTop; nRow<=nBottom; nRow++)
bRowMarked[nRow] = sal_True;
if (nTop == nMultiStart && nBottom == nMultiEnd)
break; // for, all relevant rows marked
}
if (nTop == nMultiStart && nBottom == nMultiEnd)
{
pRanges[0] = nTop;
pRanges[1] = nBottom;
delete[] bRowMarked;
return 1;
}
// Combine to ranges of rows.
SCCOLROW nRangeCnt = 0;
SCCOLROW nStart = nMultiStart;
while (nStart <= nMultiEnd)
{
while (nStart < nMultiEnd && !bRowMarked[nStart])
++nStart;
if (bRowMarked[nStart])
{
SCCOLROW nEnd = nStart;
while (nEnd < nMultiEnd && bRowMarked[nEnd])
++nEnd;
if (!bRowMarked[nEnd])
--nEnd;
pRanges[2*nRangeCnt ] = nStart;
pRanges[2*nRangeCnt+1] = nEnd;
++nRangeCnt;
nStart = nEnd+1;
}
else
nStart = nMultiEnd+1;
}
delete[] bRowMarked;
return nRangeCnt;
}
sal_Bool ScMarkData::IsAllMarked( const ScRange& rRange ) const
{
if ( !bMultiMarked )
return sal_False;
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
SCCOL nStartCol = rRange.aStart.Col();
SCROW nStartRow = rRange.aStart.Row();
SCCOL nEndCol = rRange.aEnd.Col();
SCROW nEndRow = rRange.aEnd.Row();
sal_Bool bOk = sal_True;
for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++)
if ( !pMultiSel[nCol].IsAllMarked( nStartRow, nEndRow ) )
bOk = sal_False;
return bOk;
}
SCsROW ScMarkData::GetNextMarked( SCCOL nCol, SCsROW nRow, sal_Bool bUp ) const
{
if ( !bMultiMarked )
return nRow;
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
return pMultiSel[nCol].GetNextMarked( nRow, bUp );
}
sal_Bool ScMarkData::HasMultiMarks( SCCOL nCol ) const
{
if ( !bMultiMarked )
return sal_False;
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
return pMultiSel[nCol].HasMarks();
}
sal_Bool ScMarkData::HasAnyMultiMarks() const
{
if ( !bMultiMarked )
return sal_False;
DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0");
for (SCCOL nCol=0; nCol<=MAXCOL; nCol++)
if ( pMultiSel[nCol].HasMarks() )
return sal_True;
return sal_False; // nix
}
void ScMarkData::InsertTab( SCTAB nTab )
{
for (SCTAB i=MAXTAB; i>nTab; i--)
bTabMarked[i] = bTabMarked[i-1];
bTabMarked[nTab] = sal_False;
}
void ScMarkData::DeleteTab( SCTAB nTab )
{
for (SCTAB i=nTab; i<MAXTAB; i++)
bTabMarked[i] = bTabMarked[i+1];
bTabMarked[MAXTAB] = sal_False;
}