/* -*- 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);
if (bMultiMarked)
{
aMultiRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
aMultiSel.ShiftCols(nStartCol, nColOffset);
}
}
void ScMarkData::ShiftRows(const ScDocument& rDoc, SCROW nStartRow, sal_Int32 nRowOffset)
{
if (bMarked)
aMarkRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
if (bMultiMarked)
{
aMultiRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
aMultiSel.ShiftRows(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: */
↑ V1051 Consider checking for misprints. It's possible that the 'nBottomPrev' should be checked here.