diff options
Diffstat (limited to 'sc/source/core/data/markdata.cxx')
-rw-r--r-- | sc/source/core/data/markdata.cxx | 950 |
1 files changed, 950 insertions, 0 deletions
diff --git a/sc/source/core/data/markdata.cxx b/sc/source/core/data/markdata.cxx new file mode 100644 index 000000000..54379a554 --- /dev/null +++ b/sc/source/core/data/markdata.cxx @@ -0,0 +1,950 @@ +/* -*- 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: */ |