Files
loongoffice/sc/source/core/data/markdata.cxx
Luboš Luňák 17bcf1073b introduce ScColumnData for ScColumn/ScTable code sharing
Since columns are allocated dynamically on demand, up until
this commit ScTable had ScPatternAttr member for unallocated
columns that it was using in place of those unallocated columns.
But that meant either copy&pasting pieces of code from ScColumn,
or having extra slightly more cumbersome shared function
in ScColumn, or even nothing.

Improve this by creating ScColumnData, which will be used as
ScColumn base class to keep things working as before, but
ScTable now also contains one ScColumnData instance and
so it can also call its functions.

Change-Id: If1d842c4a5aec32e2a7729358006d79cd831349f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/131034
Tested-by: Jenkins
Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
2022-03-05 08:30:15 +01:00

951 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <memory>
#include <markdata.hxx>
#include <markarr.hxx>
#include <markmulti.hxx>
#include <rangelst.hxx>
#include <segmenttree.hxx>
#include <sheetlimits.hxx>
#include <document.hxx>
#include <columnspanset.hxx>
#include <fstalgorithm.hxx>
#include <unordered_map>
#include <osl/diagnose.h>
#include <mdds/flat_segment_tree.hpp>
#include <cassert>
ScMarkData::ScMarkData(const ScSheetLimits& rSheetLimits) :
aMultiSel(rSheetLimits),
mrSheetLimits(rSheetLimits)
{
ResetMark();
}
ScMarkData& ScMarkData::operator=(const ScMarkData& rOther)
{
maTabMarked = rOther.maTabMarked;
aMarkRange = rOther.aMarkRange;
aMultiRange = rOther.aMultiRange;
aMultiSel = rOther.aMultiSel;
aTopEnvelope = rOther.aTopEnvelope;
aBottomEnvelope = rOther.aBottomEnvelope;
aLeftEnvelope = rOther.aLeftEnvelope;
aRightEnvelope = rOther.aRightEnvelope;
bMarked = rOther.bMarked;
bMultiMarked = rOther.bMultiMarked;
bMarking = rOther.bMarking;
bMarkIsNeg = rOther.bMarkIsNeg;
return *this;
}
ScMarkData& ScMarkData::operator=(ScMarkData&& rOther)
{
maTabMarked = std::move(rOther.maTabMarked);
aMarkRange = std::move(rOther.aMarkRange);
aMultiRange = std::move(rOther.aMultiRange);
aMultiSel = std::move(rOther.aMultiSel);
aTopEnvelope = std::move(rOther.aTopEnvelope);
aBottomEnvelope = std::move(rOther.aBottomEnvelope);
aLeftEnvelope = std::move(rOther.aLeftEnvelope);
aRightEnvelope = std::move(rOther.aRightEnvelope);
bMarked = rOther.bMarked;
bMultiMarked = rOther.bMultiMarked;
bMarking = rOther.bMarking;
bMarkIsNeg = rOther.bMarkIsNeg;
return *this;
}
void ScMarkData::ResetMark()
{
aMultiSel.Clear();
bMarked = bMultiMarked = false;
bMarking = bMarkIsNeg = false;
aTopEnvelope.RemoveAll();
aBottomEnvelope.RemoveAll();
aLeftEnvelope.RemoveAll();
aRightEnvelope.RemoveAll();
}
void ScMarkData::SetMarkArea( const ScRange& rRange )
{
aMarkRange = rRange;
aMarkRange.PutInOrder();
if ( !bMarked )
{
// Upon creation of a document ScFormatShell GetTextAttrState
// may query (default) attributes although no sheet is marked yet.
// => mark that one.
if ( !GetSelectCount() )
maTabMarked.insert( aMarkRange.aStart.Tab() );
bMarked = true;
}
}
void ScMarkData::SetMultiMarkArea( const ScRange& rRange, bool bMark, bool bSetupMulti )
{
if ( aMultiSel.IsEmpty() )
{
// if simple mark range is set, copy to multi marks
if ( bMarked && !bMarkIsNeg && !bSetupMulti )
{
bMarked = false;
SCCOL nStartCol = aMarkRange.aStart.Col();
SCCOL nEndCol = aMarkRange.aEnd.Col();
PutInOrder( nStartCol, nEndCol );
SetMultiMarkArea( aMarkRange, true, 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 );
aMultiSel.SetMarkArea( nStartCol, nEndCol, nStartRow, nEndRow, bMark );
if ( bMultiMarked ) // Update aMultiRange
{
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; // new
bMultiMarked = true;
}
}
void ScMarkData::SetAreaTab( SCTAB nTab )
{
aMarkRange.aStart.SetTab(nTab);
aMarkRange.aEnd.SetTab(nTab);
aMultiRange.aStart.SetTab(nTab);
aMultiRange.aEnd.SetTab(nTab);
}
void ScMarkData::SelectTable( SCTAB nTab, bool bNew )
{
if ( bNew )
{
maTabMarked.insert( nTab );
}
else
{
maTabMarked.erase( nTab );
}
}
bool ScMarkData::GetTableSelect( SCTAB nTab ) const
{
return (maTabMarked.find( nTab ) != maTabMarked.end());
}
void ScMarkData::SelectOneTable( SCTAB nTab )
{
maTabMarked.clear();
maTabMarked.insert( nTab );
}
SCTAB ScMarkData::GetSelectCount() const
{
return static_cast<SCTAB> ( maTabMarked.size() );
}
SCTAB ScMarkData::GetFirstSelected() const
{
if (!maTabMarked.empty())
return (*maTabMarked.begin());
OSL_FAIL("GetFirstSelected: nothing selected");
return 0;
}
SCTAB ScMarkData::GetLastSelected() const
{
if (!maTabMarked.empty())
return (*maTabMarked.rbegin());
OSL_FAIL("GetLastSelected: nothing selected");
return 0;
}
void ScMarkData::SetSelectedTabs(const MarkedTabsType& rTabs)
{
MarkedTabsType aTabs(rTabs.begin(), rTabs.end());
maTabMarked.swap(aTabs);
}
void ScMarkData::MarkToMulti()
{
if ( bMarked && !bMarking )
{
SetMultiMarkArea( aMarkRange, !bMarkIsNeg );
bMarked = 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 )
return;
ScRange aNew = aMultiRange;
bool bOk = false;
SCCOL nStartCol = aNew.aStart.Col();
SCCOL nEndCol = aNew.aEnd.Col();
while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nStartCol ) )
++nStartCol;
while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nEndCol ) )
--nEndCol;
// Rows are only taken from MarkArray
SCROW nStartRow, nEndRow;
if ( aMultiSel.HasOneMark( nStartCol, nStartRow, nEndRow ) )
{
bOk = true;
SCROW nCmpStart, nCmpEnd;
for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++)
if ( !aMultiSel.HasOneMark( nCol, nCmpStart, nCmpEnd )
|| nCmpStart != nStartRow || nCmpEnd != nEndRow )
bOk = false;
}
if (bOk)
{
aNew.aStart.SetCol(nStartCol);
aNew.aStart.SetRow(nStartRow);
aNew.aEnd.SetCol(nEndCol);
aNew.aEnd.SetRow(nEndRow);
ResetMark();
aMarkRange = aNew;
bMarked = true;
bMarkIsNeg = false;
}
}
bool ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, 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 true;
if (bMultiMarked)
{
//TODO: test here for negative Marking ?
return aMultiSel.GetMark( nCol, nRow );
}
return false;
}
bool ScMarkData::IsColumnMarked( SCCOL nCol ) const
{
// bMarkIsNeg meanwhile also for columns heads
//TODO: GetMarkColumnRanges for completely marked column
if ( bMarked && !bMarkIsNeg &&
aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == mrSheetLimits.mnMaxRow )
return true;
if ( bMultiMarked && aMultiSel.IsAllMarked( nCol, 0, mrSheetLimits.mnMaxRow ) )
return true;
return false;
}
bool ScMarkData::IsRowMarked( SCROW nRow ) const
{
// bMarkIsNeg meanwhile also for row heads
//TODO: GetMarkRowRanges for completely marked rows
if ( bMarked && !bMarkIsNeg &&
aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == mrSheetLimits.mnMaxCol &&
aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
return true;
if ( bMultiMarked )
return aMultiSel.IsRowMarked( nRow );
return false;
}
void ScMarkData::MarkFromRangeList( const ScRangeList& rList, bool bReset )
{
if (bReset)
{
maTabMarked.clear();
ResetMark();
}
size_t nCount = rList.size();
if ( nCount == 1 && !bMarked && !bMultiMarked )
{
const ScRange& rRange = rList[ 0 ];
SetMarkArea( rRange );
SelectTable( rRange.aStart.Tab(), true );
}
else
{
for (size_t i=0; i < nCount; i++)
{
const ScRange& rRange = rList[ i ];
SetMultiMarkArea( rRange );
SelectTable( rRange.aStart.Tab(), true );
}
}
}
/**
Optimise the case of constructing from a range list, speeds up import.
*/
ScMarkData::ScMarkData(const ScSheetLimits& rLimits, const ScRangeList& rList)
: aMultiSel(rLimits),
mrSheetLimits(rLimits)
{
ResetMark();
for (const ScRange& rRange : rList)
maTabMarked.insert( rRange.aStart.Tab() );
if (rList.size() > 1)
{
bMultiMarked = true;
aMultiRange = rList.Combine();
aMultiSel.Set( rList );
}
else if (rList.size() == 1)
{
const ScRange& rRange = rList[ 0 ];
SetMarkArea( rRange );
}
}
void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, bool bClear, SCTAB nForTab ) const
{
if (!pList)
return;
if (bClear)
pList->RemoveAll();
//TODO: for multiple selected tables enter multiple ranges !!!
if ( bMultiMarked )
{
SCTAB nTab = (nForTab < 0 ? aMultiRange.aStart.Tab() : nForTab);
SCCOL nStartCol = aMultiRange.aStart.Col();
SCCOL nEndCol = aMultiRange.aEnd.Col();
for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
{
if (aMultiSel.HasMarks( nCol ))
{
// Feeding column-wise fragments to ScRangeList::Join() is a
// huge bottleneck, speed this up for multiple columns
// consisting of identical row sets by building a column span
// first. This is usually the case for filtered data, for
// example.
SCCOL nToCol = nCol+1;
for ( ; nToCol <= nEndCol; ++nToCol)
{
if (!aMultiSel.HasEqualRowsMarked(nCol, nToCol))
break;
}
--nToCol;
ScRange aRange( nCol, 0, nTab, nToCol, 0, nTab );
SCROW nTop, nBottom;
ScMultiSelIter aMultiIter( aMultiSel, nCol );
while ( aMultiIter.Next( nTop, nBottom ) )
{
aRange.aStart.SetRow( nTop );
aRange.aEnd.SetRow( nBottom );
pList->Join( aRange );
}
nCol = nToCol;
}
}
}
if ( bMarked )
{
if (nForTab < 0)
pList->push_back( aMarkRange );
else
{
ScRange aRange( aMarkRange );
aRange.aStart.SetTab( nForTab );
aRange.aEnd.SetTab( nForTab );
pList->push_back( aRange );
}
}
}
void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const
{
if (!pList)
return;
ScRangeList aOldList(*pList);
pList->RemoveAll(); //TODO: or skip the existing below
for (const auto& rTab : maTabMarked)
for ( size_t i=0, nCount = aOldList.size(); i<nCount; i++)
{
ScRange aRange = aOldList[ i ];
aRange.aStart.SetTab(rTab);
aRange.aEnd.SetTab(rTab);
pList->push_back( aRange );
}
}
ScRangeList ScMarkData::GetMarkedRanges() const
{
ScRangeList aRet;
FillRangeListWithMarks(&aRet, false);
return aRet;
}
ScRangeList ScMarkData::GetMarkedRangesForTab( SCTAB nTab ) const
{
ScRangeList aRet;
FillRangeListWithMarks(&aRet, false, nTab);
return aRet;
}
std::vector<sc::ColRowSpan> ScMarkData::GetMarkedRowSpans() const
{
typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
ScRangeList aRanges = GetMarkedRanges();
SpansType aSpans(0, mrSheetLimits.mnMaxRow+1, false);
SpansType::const_iterator itPos = aSpans.begin();
for (size_t i = 0, n = aRanges.size(); i < n; ++i)
{
const ScRange& r = aRanges[i];
itPos = aSpans.insert(itPos, r.aStart.Row(), r.aEnd.Row()+1, true).first;
}
return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
}
std::vector<sc::ColRowSpan> ScMarkData::GetMarkedColSpans() const
{
if (bMultiMarked)
{
SCCOL nStartCol = aMultiRange.aStart.Col();
SCCOL nEndCol = aMultiRange.aEnd.Col();
if (bMarked)
{
// Use segment tree to merge marked with multi marked.
typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
SpansType aSpans(0, mrSheetLimits.mnMaxCol+1, false);
SpansType::const_iterator itPos = aSpans.begin();
do
{
if (aMultiSel.GetRowSelArray().HasMarks())
{
itPos = aSpans.insert(itPos, nStartCol, nEndCol+1, true).first;
break; // do; all columns marked
}
/* XXX if it turns out that span insert is too slow for lots of
* subsequent columns we could gather each span first and then
* insert. */
for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
{
const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
if (pMultiArray && pMultiArray->HasMarks())
itPos = aSpans.insert(itPos, nCol, nCol+1, true).first;
}
}
while(false);
// Merge marked.
aSpans.insert(itPos, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col()+1, true);
return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
}
else
{
// A plain vector is sufficient, avoid segment tree and conversion
// to vector overhead.
std::vector<sc::ColRowSpan> aVec;
if (aMultiSel.GetRowSelArray().HasMarks())
{
aVec.emplace_back( nStartCol, nEndCol);
return aVec; // all columns marked
}
sc::ColRowSpan aSpan( -1, -1);
for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
{
const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
if (pMultiArray && pMultiArray->HasMarks())
{
if (aSpan.mnStart == -1)
aSpan.mnStart = nCol;
aSpan.mnEnd = nCol;
}
else
{
// Add span gathered so far, if any.
if (aSpan.mnStart != -1)
{
aVec.push_back( aSpan);
aSpan.mnStart = -1;
}
}
}
// Add last span, if any.
if (aSpan.mnStart != -1)
aVec.push_back( aSpan);
return aVec;
}
}
// Only reached if not multi marked.
std::vector<sc::ColRowSpan> aVec;
if (bMarked)
{
aVec.emplace_back( aMarkRange.aStart.Col(), aMarkRange.aEnd.Col());
}
return aVec;
}
bool ScMarkData::IsAllMarked( const ScRange& rRange ) const
{
SCCOL nStartCol = rRange.aStart.Col();
SCROW nStartRow = rRange.aStart.Row();
SCCOL nEndCol = rRange.aEnd.Col();
SCROW nEndRow = rRange.aEnd.Row();
if ( !bMultiMarked )
{
if ( bMarked && !bMarkIsNeg &&
aMarkRange.aStart.Col() <= nStartCol && aMarkRange.aEnd.Col() >= nEndCol &&
aMarkRange.aStart.Row() <= nStartRow && aMarkRange.aEnd.Row() >= nEndRow )
return true;
return false;
}
bool bOk = true;
if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
return aMultiSel.IsRowRangeMarked( nStartRow, nEndRow );
for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++)
if ( !aMultiSel.IsAllMarked( nCol, nStartRow, nEndRow ) )
bOk = false;
return bOk;
}
SCCOL ScMarkData::GetStartOfEqualColumns( SCCOL nLastCol, SCCOL nMinCol ) const
{
if( !bMultiMarked )
{
if ( bMarked && !bMarkIsNeg )
{
if( aMarkRange.aEnd.Col() >= nMinCol && aMarkRange.aStart.Col() < nLastCol )
return aMarkRange.aEnd.Col() + 1;
if( aMarkRange.aEnd.Col() >= nLastCol && aMarkRange.aStart.Col() <= nMinCol )
return aMarkRange.aStart.Col();
}
return nMinCol;
}
return aMultiSel.GetStartOfEqualColumns( nLastCol, nMinCol );
}
SCROW ScMarkData::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
{
if ( !bMultiMarked )
return nRow;
return aMultiSel.GetNextMarked( nCol, nRow, bUp );
}
bool ScMarkData::HasMultiMarks( SCCOL nCol ) const
{
if ( !bMultiMarked )
return false;
return aMultiSel.HasMarks( nCol );
}
bool ScMarkData::HasAnyMultiMarks() const
{
if ( !bMultiMarked )
return false;
return aMultiSel.HasAnyMarks();
}
void ScMarkData::InsertTab( SCTAB nTab )
{
std::set<SCTAB> tabMarked;
for (const auto& rTab : maTabMarked)
{
if (rTab < nTab)
tabMarked.insert(rTab);
else
tabMarked.insert(rTab + 1);
}
maTabMarked.swap(tabMarked);
}
void ScMarkData::DeleteTab( SCTAB nTab )
{
std::set<SCTAB> tabMarked;
for (const auto& rTab : maTabMarked)
{
if (rTab < nTab)
tabMarked.insert(rTab);
else if (rTab > nTab)
tabMarked.insert(rTab - 1);
}
maTabMarked.swap(tabMarked);
}
void ScMarkData::ShiftCols(const ScDocument& rDoc, SCCOL nStartCol, sal_Int32 nColOffset)
{
if (bMarked)
{
aMarkRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
}
else if (bMultiMarked)
{
aMultiSel.ShiftCols(nStartCol, nColOffset);
aMultiRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
}
}
void ScMarkData::ShiftRows(const ScDocument& rDoc, SCROW nStartRow, sal_Int32 nRowOffset)
{
if (bMarked)
{
aMarkRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
}
else if (bMultiMarked)
{
aMultiSel.ShiftRows(nStartRow, nRowOffset);
aMultiRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
}
}
static void lcl_AddRanges(ScRange& rRangeDest, const ScRange& rNewRange )
{
SCCOL nStartCol = rNewRange.aStart.Col();
SCROW nStartRow = rNewRange.aStart.Row();
SCCOL nEndCol = rNewRange.aEnd.Col();
SCROW nEndRow = rNewRange.aEnd.Row();
PutInOrder( nStartRow, nEndRow );
PutInOrder( nStartCol, nEndCol );
if ( nStartCol < rRangeDest.aStart.Col() )
rRangeDest.aStart.SetCol( nStartCol );
if ( nStartRow < rRangeDest.aStart.Row() )
rRangeDest.aStart.SetRow( nStartRow );
if ( nEndCol > rRangeDest.aEnd.Col() )
rRangeDest.aEnd.SetCol( nEndCol );
if ( nEndRow > rRangeDest.aEnd.Row() )
rRangeDest.aEnd.SetRow( nEndRow );
}
void ScMarkData::GetSelectionCover( ScRange& rRange )
{
if( bMultiMarked )
{
rRange = aMultiRange;
SCCOL nStartCol = aMultiRange.aStart.Col(), nEndCol = aMultiRange.aEnd.Col();
PutInOrder( nStartCol, nEndCol );
nStartCol = ( nStartCol == 0 ) ? nStartCol : nStartCol - 1;
nEndCol = ( nEndCol == mrSheetLimits.mnMaxCol ) ? nEndCol : nEndCol + 1;
std::unique_ptr<ScFlatBoolRowSegments> pPrevColMarkedRows;
std::unique_ptr<ScFlatBoolRowSegments> pCurColMarkedRows;
std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInTopEnvelope;
std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInBottomEnvelope;
ScFlatBoolRowSegments aNoRowsMarked(mrSheetLimits.mnMaxRow);
aNoRowsMarked.setFalse( 0, mrSheetLimits.mnMaxRow );
bool bPrevColUnMarked = false;
for ( SCCOL nCol=nStartCol; nCol <= nEndCol; nCol++ )
{
SCROW nTop, nBottom;
bool bCurColUnMarked = !aMultiSel.HasMarks( nCol );
if ( !bCurColUnMarked )
{
pCurColMarkedRows.reset( new ScFlatBoolRowSegments(mrSheetLimits.mnMaxRow) );
pCurColMarkedRows->setFalse( 0, mrSheetLimits.mnMaxRow );
ScMultiSelIter aMultiIter( aMultiSel, nCol );
ScFlatBoolRowSegments::ForwardIterator aPrevItr(
pPrevColMarkedRows ? *pPrevColMarkedRows
: aNoRowsMarked); // For finding left envelope
ScFlatBoolRowSegments::ForwardIterator aPrevItr1(
pPrevColMarkedRows ? *pPrevColMarkedRows
: aNoRowsMarked); // For finding right envelope
SCROW nTopPrev = 0, nBottomPrev = 0; // For right envelope
while ( aMultiIter.Next( nTop, nBottom ) )
{
pCurColMarkedRows->setTrue( nTop, nBottom );
if( bPrevColUnMarked && ( nCol > nStartCol ))
{
ScRange aAddRange(nCol - 1, nTop, aMultiRange.aStart.Tab(),
nCol - 1, nBottom, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Left envelope
aLeftEnvelope.push_back( aAddRange );
}
else if( nCol > nStartCol )
{
SCROW nTop1 = nTop, nBottom1 = nTop;
while( nTop1 <= nBottom && nBottom1 <= nBottom )
{
bool bRangeMarked = false;
const bool bHasValue = aPrevItr.getValue( nTop1, bRangeMarked );
assert(bHasValue); (void)bHasValue;
if( bRangeMarked )
{
nTop1 = aPrevItr.getLastPos() + 1;
nBottom1 = nTop1;
}
else
{
nBottom1 = aPrevItr.getLastPos();
if( nBottom1 > nBottom )
nBottom1 = nBottom;
ScRange aAddRange( nCol - 1, nTop1, aMultiRange.aStart.Tab(),
nCol - 1, nBottom1, aMultiRange.aStart.Tab() );
lcl_AddRanges( rRange, aAddRange ); // Left envelope
aLeftEnvelope.push_back( aAddRange );
nTop1 = ++nBottom1;
}
}
while( nTopPrev <= nBottom && nBottomPrev <= nBottom )
{
bool bRangeMarked;
const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
assert(bHasValue); (void)bHasValue;
if( bRangeMarked )
{
nBottomPrev = aPrevItr1.getLastPos();
if( nTopPrev < nTop )
{
if( nBottomPrev >= nTop )
{
nBottomPrev = nTop - 1;
ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
nCol, nBottomPrev, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Right envelope
aRightEnvelope.push_back( aAddRange );
nTopPrev = nBottomPrev = (nBottom + 1);
}
else
{
ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
nCol, nBottomPrev, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Right envelope
aRightEnvelope.push_back( aAddRange );
nTopPrev = ++nBottomPrev;
}
}
else
nTopPrev = nBottomPrev = ( nBottom + 1 );
}
else
{
nBottomPrev = aPrevItr1.getLastPos();
nTopPrev = ++nBottomPrev;
}
}
}
if( nTop )
{
ScRange aAddRange( nCol, nTop - 1, aMultiRange.aStart.Tab(),
nCol, nTop - 1, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Top envelope
auto it = aRowToColSegmentsInTopEnvelope.find(nTop - 1);
if (it == aRowToColSegmentsInTopEnvelope.end())
it = aRowToColSegmentsInTopEnvelope.emplace(nTop - 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
it->second.setTrue( nCol, nCol );
}
if( nBottom < mrSheetLimits.mnMaxRow )
{
ScRange aAddRange(nCol, nBottom + 1, aMultiRange.aStart.Tab(),
nCol, nBottom + 1, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Bottom envelope
auto it = aRowToColSegmentsInBottomEnvelope.find(nBottom + 1);
if (it == aRowToColSegmentsInBottomEnvelope.end())
it = aRowToColSegmentsInBottomEnvelope.emplace(nBottom + 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
it->second.setTrue( nCol, nCol );
}
}
while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow && ( nCol > nStartCol ) )
{
bool bRangeMarked;
const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
assert(bHasValue); (void)bHasValue;
if( bRangeMarked )
{
nBottomPrev = aPrevItr1.getLastPos();
ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
nCol, nBottomPrev, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Right envelope
aRightEnvelope.push_back( aAddRange );
nTopPrev = ++nBottomPrev;
}
else
{
nBottomPrev = aPrevItr1.getLastPos();
nTopPrev = ++nBottomPrev;
}
}
}
else if( nCol > nStartCol )
{
bPrevColUnMarked = true;
SCROW nTopPrev = 0, nBottomPrev = 0;
bool bRangeMarked = false;
ScFlatBoolRowSegments::ForwardIterator aPrevItr(
pPrevColMarkedRows ? *pPrevColMarkedRows : aNoRowsMarked);
while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow )
{
const bool bHasValue = aPrevItr.getValue(nTopPrev, bRangeMarked);
assert(bHasValue); (void)bHasValue;
if( bRangeMarked )
{
nBottomPrev = aPrevItr.getLastPos();
ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
nCol, nBottomPrev, aMultiRange.aStart.Tab());
lcl_AddRanges( rRange, aAddRange ); // Right envelope
aRightEnvelope.push_back( aAddRange );
nTopPrev = ++nBottomPrev;
}
else
{
nBottomPrev = aPrevItr.getLastPos();
nTopPrev = ++nBottomPrev;
}
}
}
if ( bCurColUnMarked )
pPrevColMarkedRows.reset();
else
pPrevColMarkedRows = std::move( pCurColMarkedRows );
}
for( auto& rKV : aRowToColSegmentsInTopEnvelope )
{
SCCOL nStart = nStartCol;
ScFlatBoolColSegments::RangeData aRange;
while( nStart <= nEndCol )
{
if( !rKV.second.getRangeData( nStart, aRange ) )
break;
if( aRange.mbValue ) // is marked
aTopEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
nStart = aRange.mnCol2 + 1;
}
}
for( auto& rKV : aRowToColSegmentsInBottomEnvelope )
{
SCCOL nStart = nStartCol;
ScFlatBoolColSegments::RangeData aRange;
while( nStart <= nEndCol )
{
if( !rKV.second.getRangeData( nStart, aRange ) )
break;
if( aRange.mbValue ) // is marked
aBottomEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
nStart = aRange.mnCol2 + 1;
}
}
}
else if( bMarked )
{
aMarkRange.PutInOrder();
SCROW nRow1, nRow2, nRow1New, nRow2New;
SCCOL nCol1, nCol2, nCol1New, nCol2New;
SCTAB nTab1, nTab2;
aMarkRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
nCol1New = nCol1;
nCol2New = nCol2;
nRow1New = nRow1;
nRow2New = nRow2;
// Each envelope will have zero or more ranges for single rectangle selection.
if( nCol1 > 0 )
{
aLeftEnvelope.push_back( ScRange( nCol1 - 1, nRow1, nTab1, nCol1 - 1, nRow2, nTab2 ) );
--nCol1New;
}
if( nRow1 > 0 )
{
aTopEnvelope.push_back( ScRange( nCol1, nRow1 - 1, nTab1, nCol2, nRow1 - 1, nTab2 ) );
--nRow1New;
}
if( nCol2 < mrSheetLimits.mnMaxCol )
{
aRightEnvelope.push_back( ScRange( nCol2 + 1, nRow1, nTab1, nCol2 + 1, nRow2, nTab2 ) );
++nCol2New;
}
if( nRow2 < mrSheetLimits.mnMaxRow )
{
aBottomEnvelope.push_back( ScRange( nCol1, nRow2 + 1, nTab1, nCol2, nRow2 + 1, nTab2 ) );
++nRow2New;
}
rRange = ScRange( nCol1New, nRow1New, nTab1, nCol2New, nRow2New, nTab2 );
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */