diff options
Diffstat (limited to 'sc/source/core/data/columnspanset.cxx')
-rw-r--r-- | sc/source/core/data/columnspanset.cxx | 369 |
1 files changed, 369 insertions, 0 deletions
diff --git a/sc/source/core/data/columnspanset.cxx b/sc/source/core/data/columnspanset.cxx new file mode 100644 index 000000000..eb09ea26b --- /dev/null +++ b/sc/source/core/data/columnspanset.cxx @@ -0,0 +1,369 @@ +/* -*- 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/. + */ + +#include <columnspanset.hxx> +#include <column.hxx> +#include <table.hxx> +#include <document.hxx> +#include <mtvfunctions.hxx> +#include <markdata.hxx> +#include <rangelst.hxx> +#include <fstalgorithm.hxx> + +#include <algorithm> +#include <memory> + +#include <o3tl/safeint.hxx> + +namespace sc { + +namespace { + +class ColumnNonEmptyRangesScanner +{ + ColumnSpanSet::ColumnSpansType& mrRanges; + bool mbVal; +public: + ColumnNonEmptyRangesScanner(ColumnSpanSet::ColumnSpansType& rRanges, bool bVal) : + mrRanges(rRanges), mbVal(bVal) {} + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) + { + if (node.type == sc::element_type_empty) + return; + + size_t nRow = node.position + nOffset; + size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1 + mrRanges.insert_back(nRow, nEndRow, mbVal); + } +}; + +} + +RowSpan::RowSpan(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {} + +ColRowSpan::ColRowSpan(SCCOLROW nStart, SCCOLROW nEnd) : mnStart(nStart), mnEnd(nEnd) {} + +ColumnSpanSet::ColumnType::ColumnType(SCROW nStart, SCROW nEnd, bool bInit) : + maSpans(nStart, nEnd+1, bInit), miPos(maSpans.begin()) {} + +ColumnSpanSet::Action::~Action() {} +void ColumnSpanSet::Action::startColumn(SCTAB /*nTab*/, SCCOL /*nCol*/) {} + +ColumnSpanSet::ColumnAction::~ColumnAction() {} + +ColumnSpanSet::ColumnSpanSet() {} + +ColumnSpanSet::~ColumnSpanSet() +{ +} + +ColumnSpanSet::ColumnType& ColumnSpanSet::getColumn(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol) +{ + if (o3tl::make_unsigned(nTab) >= maTables.size()) + maTables.resize(nTab+1); + + TableType& rTab = maTables[nTab]; + if (o3tl::make_unsigned(nCol) >= rTab.size()) + rTab.resize(nCol+1); + + if (!rTab[nCol]) + rTab[nCol].emplace(0, rDoc.MaxRow(), /*bInit*/false); + + return *rTab[nCol]; +} + +void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal) +{ + if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow)) + return; + + ColumnType& rCol = getColumn(rDoc, nTab, nCol); + rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow, nRow+1, bVal).first; +} + +void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, bool bVal) +{ + if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow1) || !rDoc.ValidRow(nRow2)) + return; + + ColumnType& rCol = getColumn(rDoc, nTab, nCol); + rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow1, nRow2+1, bVal).first; +} + +void ColumnSpanSet::set(const ScDocument& rDoc, const ScRange& rRange, bool bVal) +{ + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol) + { + ColumnType& rCol = getColumn(rDoc, nTab, nCol); + rCol.miPos = rCol.maSpans.insert(rCol.miPos, rRange.aStart.Row(), rRange.aEnd.Row()+1, bVal).first; + } + } +} + +void ColumnSpanSet::set( const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SingleColumnSpanSet& rSingleSet, bool bVal ) +{ + SingleColumnSpanSet::SpansType aSpans; + rSingleSet.getSpans(aSpans); + for (const auto& rSpan : aSpans) + set(rDoc, nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, bVal); +} + +void ColumnSpanSet::scan( + const ScDocument& rDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal) +{ + if (!rDoc.ValidColRow(nCol1, nRow1) || !rDoc.ValidColRow(nCol2, nRow2)) + return; + + if (nCol1 > nCol2 || nRow1 > nRow2) + return; + + const ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + return; + + nCol2 = pTab->ClampToAllocatedColumns(nCol2); + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + ColumnType& rCol = getColumn(rDoc, nTab, nCol); + + const CellStoreType& rSrcCells = pTab->aCol[nCol].maCells; + + if( nRow1 > pTab->aCol[nCol].GetLastDataPos()) + continue; + + ColumnNonEmptyRangesScanner aScanner(rCol.maSpans, bVal); + ParseBlock(rSrcCells.begin(), rSrcCells, aScanner, nRow1, nRow2); + } +} + +void ColumnSpanSet::executeAction(ScDocument& rDoc, Action& ac) const +{ + for (size_t nTab = 0; nTab < maTables.size(); ++nTab) + { + if (maTables[nTab].empty()) + continue; + + ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + continue; + + const TableType& rTab = maTables[nTab]; + for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol) + { + if (!rTab[nCol]) + continue; + if (nCol >= pTab->GetAllocatedColumnsCount()) + break; + + ac.startColumn(nTab, nCol); + const ColumnType& rCol = *rTab[nCol]; + ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end(); + SCROW nRow1, nRow2; + nRow1 = it->first; + bool bVal = it->second; + for (++it; it != itEnd; ++it) + { + nRow2 = it->first-1; + ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal); + + nRow1 = nRow2+1; // for the next iteration. + bVal = it->second; + } + } + } +} + +void ColumnSpanSet::executeColumnAction(ScDocument& rDoc, ColumnAction& ac) const +{ + for (size_t nTab = 0; nTab < maTables.size(); ++nTab) + { + if (maTables[nTab].empty()) + continue; + + ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + continue; + + const TableType& rTab = maTables[nTab]; + for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol) + { + if (!rTab[nCol]) + continue; + if (nCol >= pTab->GetAllocatedColumnsCount()) + break; + + ScColumn& rColumn = pTab->aCol[nCol]; + ac.startColumn(&rColumn); + const ColumnType& rCol = *rTab[nCol]; + ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end(); + SCROW nRow1, nRow2; + nRow1 = it->first; + bool bVal = it->second; + for (++it; it != itEnd; ++it) + { + nRow2 = it->first-1; + ac.execute(nRow1, nRow2, bVal); + + nRow1 = nRow2+1; // for the next iteration. + bVal = it->second; + } + } + } +} + +namespace { + +class NonEmptyRangesScanner +{ + SingleColumnSpanSet::ColumnSpansType& mrRanges; +public: + explicit NonEmptyRangesScanner(SingleColumnSpanSet::ColumnSpansType& rRanges) : mrRanges(rRanges) {} + + void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize) + { + if (node.type == sc::element_type_empty) + return; + + size_t nRow = node.position + nOffset; + size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1 + mrRanges.insert_back(nRow, nEndRow, true); + } +}; + +} + +SingleColumnSpanSet::SingleColumnSpanSet(ScSheetLimits const & rSheetLimits) + : mrSheetLimits(rSheetLimits), + maSpans(0, rSheetLimits.GetMaxRowCount(), false) {} + +void SingleColumnSpanSet::scan(const ScColumn& rColumn) +{ + const CellStoreType& rCells = rColumn.maCells; + SCROW nCurRow = 0; + for (const auto& rCell : rCells) + { + SCROW nEndRow = nCurRow + rCell.size; // Last row of current block plus 1. + if (rCell.type != sc::element_type_empty) + maSpans.insert_back(nCurRow, nEndRow, true); + + nCurRow = nEndRow; + } +} + +void SingleColumnSpanSet::scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd) +{ + if( nStart > rColumn.GetLastDataPos()) + return; + const CellStoreType& rCells = rColumn.maCells; + NonEmptyRangesScanner aScanner(maSpans); + sc::ParseBlock(rCells.begin(), rCells, aScanner, nStart, nEnd); +} + +void SingleColumnSpanSet::scan( + ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd) +{ + if( nStart > rColumn.GetLastDataPos()) + return; + const CellStoreType& rCells = rColumn.maCells; + NonEmptyRangesScanner aScanner(maSpans); + rBlockPos.miCellPos = sc::ParseBlock(rBlockPos.miCellPos, rCells, aScanner, nStart, nEnd); +} + +void SingleColumnSpanSet::scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol) +{ + if (!rMark.GetTableSelect(nTab)) + // This table is not selected. Nothing to scan. + return; + + ScRangeList aRanges = rMark.GetMarkedRangesForTab(nTab); + scan(aRanges, nTab, nCol); +} + +void SingleColumnSpanSet::scan(const ScRangeList& rRanges, SCTAB nTab, SCCOL nCol) +{ + for (size_t i = 0, n = rRanges.size(); i < n; ++i) + { + const ScRange & rRange = rRanges[i]; + if (nTab < rRange.aStart.Tab() || rRange.aEnd.Tab() < nTab) + continue; + + if (nCol < rRange.aStart.Col() || rRange.aEnd.Col() < nCol) + // This column is not in this range. Skip it. + continue; + + maSpans.insert_back(rRange.aStart.Row(), rRange.aEnd.Row()+1, true); + } +} + +void SingleColumnSpanSet::set(SCROW nRow1, SCROW nRow2, bool bVal) +{ + maSpans.insert_back(nRow1, nRow2+1, bVal); +} + +void SingleColumnSpanSet::getRows(std::vector<SCROW> &rRows) const +{ + std::vector<SCROW> aRows; + + SpansType aRanges; + getSpans(aRanges); + for (const auto& rRange : aRanges) + { + for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow) + aRows.push_back(nRow); + } + + rRows.swap(aRows); +} + +void SingleColumnSpanSet::getSpans(SpansType& rSpans) const +{ + SpansType aSpans = toSpanArray<SCROW,RowSpan>(maSpans); + rSpans.swap(aSpans); +} + +void SingleColumnSpanSet::swap( SingleColumnSpanSet& r ) +{ + maSpans.swap(r.maSpans); +} + +bool SingleColumnSpanSet::empty() const +{ + // Empty if there's only the 0..rDoc.MaxRow() span with false. + ColumnSpansType::const_iterator it = maSpans.begin(); + return (it->first == 0) && !(it->second) && (++it != maSpans.end()) && (it->first == mrSheetLimits.GetMaxRowCount()); +} + + +void RangeColumnSpanSet::executeColumnAction(ScDocument& rDoc, sc::ColumnSpanSet::ColumnAction& ac) const +{ + for (SCTAB nTab = range.aStart.Tab(); nTab <= range.aEnd.Tab(); ++nTab) + { + ScTable* pTab = rDoc.FetchTable(nTab); + if (!pTab) + continue; + + SCCOL nEndCol = pTab->ClampToAllocatedColumns(range.aEnd.Col()); + for (SCCOL nCol = range.aStart.Col(); nCol <= nEndCol; ++nCol) + { + if (!rDoc.ValidCol(nCol)) + break; + + ScColumn& rColumn = pTab->aCol[nCol]; + ac.startColumn(&rColumn); + ac.execute( range.aStart.Row(), range.aEnd.Row(), true ); + } + } +} + +} // namespace sc + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |