Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / columnspanset.cxx
blob69377bdba76cc6da9d098a1e02c6635ab88435c7
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <columnspanset.hxx>
11 #include <column.hxx>
12 #include <table.hxx>
13 #include <document.hxx>
14 #include <mtvfunctions.hxx>
15 #include <markdata.hxx>
16 #include <rangelst.hxx>
17 #include <fstalgorithm.hxx>
19 #include <algorithm>
21 #include <o3tl/safeint.hxx>
23 namespace sc {
25 namespace {
27 class ColumnNonEmptyRangesScanner
29 ColumnSpanSet::ColumnSpansType& mrRanges;
30 bool mbVal;
31 public:
32 ColumnNonEmptyRangesScanner(ColumnSpanSet::ColumnSpansType& rRanges, bool bVal) :
33 mrRanges(rRanges), mbVal(bVal) {}
35 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
37 if (node.type == sc::element_type_empty)
38 return;
40 size_t nRow = node.position + nOffset;
41 size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
42 mrRanges.insert_back(nRow, nEndRow, mbVal);
48 RowSpan::RowSpan(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {}
50 ColRowSpan::ColRowSpan(SCCOLROW nStart, SCCOLROW nEnd) : mnStart(nStart), mnEnd(nEnd) {}
52 ColumnSpanSet::ColumnType::ColumnType(SCROW nStart, SCROW nEnd, bool bInit) :
53 maSpans(nStart, nEnd+1, bInit), miPos(maSpans.begin()) {}
55 ColumnSpanSet::ColumnType::ColumnType(const ColumnType& rOther) :
56 maSpans(rOther.maSpans), miPos(maSpans.begin()) {} // NB: copying maSpans invalidates miPos - reset it
58 ColumnSpanSet::Action::~Action() {}
59 void ColumnSpanSet::Action::startColumn(SCTAB /*nTab*/, SCCOL /*nCol*/) {}
61 ColumnSpanSet::ColumnAction::~ColumnAction() {}
63 ColumnSpanSet::ColumnSpanSet() {}
65 ColumnSpanSet::~ColumnSpanSet()
69 ColumnSpanSet::ColumnType& ColumnSpanSet::getColumn(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol)
71 if (o3tl::make_unsigned(nTab) >= maTables.size())
72 maTables.resize(nTab+1);
74 TableType& rTab = maTables[nTab];
75 if (o3tl::make_unsigned(nCol) >= rTab.size())
76 rTab.resize(nCol+1);
78 if (!rTab[nCol])
79 rTab[nCol].emplace(0, rDoc.MaxRow(), /*bInit*/false);
81 return *rTab[nCol];
84 void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
86 if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow))
87 return;
89 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
90 rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow, nRow+1, bVal).first;
93 void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, bool bVal)
95 if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow1) || !rDoc.ValidRow(nRow2))
96 return;
98 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
99 rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow1, nRow2+1, bVal).first;
102 void ColumnSpanSet::set(const ScDocument& rDoc, const ScRange& rRange, bool bVal)
104 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
106 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
108 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
109 rCol.miPos = rCol.maSpans.insert(rCol.miPos, rRange.aStart.Row(), rRange.aEnd.Row()+1, bVal).first;
114 void ColumnSpanSet::set( const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SingleColumnSpanSet& rSingleSet, bool bVal )
116 SingleColumnSpanSet::SpansType aSpans;
117 rSingleSet.getSpans(aSpans);
118 for (const auto& rSpan : aSpans)
119 set(rDoc, nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, bVal);
122 void ColumnSpanSet::scan(
123 const ScDocument& rDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal)
125 if (!rDoc.ValidColRow(nCol1, nRow1) || !rDoc.ValidColRow(nCol2, nRow2))
126 return;
128 if (nCol1 > nCol2 || nRow1 > nRow2)
129 return;
131 const ScTable* pTab = rDoc.FetchTable(nTab);
132 if (!pTab)
133 return;
135 nCol2 = pTab->ClampToAllocatedColumns(nCol2);
136 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
138 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
140 const CellStoreType& rSrcCells = pTab->aCol[nCol].maCells;
142 if( nRow1 > pTab->aCol[nCol].GetLastDataPos())
143 continue;
145 ColumnNonEmptyRangesScanner aScanner(rCol.maSpans, bVal);
146 ParseBlock(rSrcCells.begin(), rSrcCells, aScanner, nRow1, nRow2);
147 rCol.miPos = rCol.maSpans.begin();
151 void ColumnSpanSet::executeAction(ScDocument& rDoc, Action& ac) const
153 for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
155 if (maTables[nTab].empty())
156 continue;
158 ScTable* pTab = rDoc.FetchTable(nTab);
159 if (!pTab)
160 continue;
162 const TableType& rTab = maTables[nTab];
163 for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
165 if (!rTab[nCol])
166 continue;
167 if (nCol >= pTab->GetAllocatedColumnsCount())
168 break;
170 ac.startColumn(nTab, nCol);
171 const ColumnType& rCol = *rTab[nCol];
172 ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
173 SCROW nRow1, nRow2;
174 nRow1 = it->first;
175 bool bVal = it->second;
176 for (++it; it != itEnd; ++it)
178 nRow2 = it->first-1;
179 ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal);
181 nRow1 = nRow2+1; // for the next iteration.
182 bVal = it->second;
188 void ColumnSpanSet::executeColumnAction(ScDocument& rDoc, ColumnAction& ac) const
190 for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
192 if (maTables[nTab].empty())
193 continue;
195 ScTable* pTab = rDoc.FetchTable(nTab);
196 if (!pTab)
197 continue;
199 const TableType& rTab = maTables[nTab];
200 for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
202 if (!rTab[nCol])
203 continue;
204 if (nCol >= pTab->GetAllocatedColumnsCount())
205 break;
207 ScColumn& rColumn = pTab->aCol[nCol];
208 ac.startColumn(&rColumn);
209 const ColumnType& rCol = *rTab[nCol];
210 ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
211 SCROW nRow1, nRow2;
212 nRow1 = it->first;
213 bool bVal = it->second;
214 for (++it; it != itEnd; ++it)
216 nRow2 = it->first-1;
217 ac.execute(nRow1, nRow2, bVal);
219 nRow1 = nRow2+1; // for the next iteration.
220 bVal = it->second;
226 namespace {
228 class NonEmptyRangesScanner
230 SingleColumnSpanSet::ColumnSpansType& mrRanges;
231 public:
232 explicit NonEmptyRangesScanner(SingleColumnSpanSet::ColumnSpansType& rRanges) : mrRanges(rRanges) {}
234 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
236 if (node.type == sc::element_type_empty)
237 return;
239 size_t nRow = node.position + nOffset;
240 size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
241 mrRanges.insert_back(nRow, nEndRow, true);
247 SingleColumnSpanSet::SingleColumnSpanSet(ScSheetLimits const & rSheetLimits)
248 : mrSheetLimits(rSheetLimits),
249 maSpans(0, rSheetLimits.GetMaxRowCount(), false) {}
251 void SingleColumnSpanSet::scan(const ScColumn& rColumn)
253 const CellStoreType& rCells = rColumn.maCells;
254 SCROW nCurRow = 0;
255 for (const auto& rCell : rCells)
257 SCROW nEndRow = nCurRow + rCell.size; // Last row of current block plus 1.
258 if (rCell.type != sc::element_type_empty)
259 maSpans.insert_back(nCurRow, nEndRow, true);
261 nCurRow = nEndRow;
265 void SingleColumnSpanSet::scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
267 if( nStart > rColumn.GetLastDataPos())
268 return;
269 const CellStoreType& rCells = rColumn.maCells;
270 NonEmptyRangesScanner aScanner(maSpans);
271 sc::ParseBlock(rCells.begin(), rCells, aScanner, nStart, nEnd);
274 void SingleColumnSpanSet::scan(
275 ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
277 if( nStart > rColumn.GetLastDataPos())
278 return;
279 const CellStoreType& rCells = rColumn.maCells;
280 NonEmptyRangesScanner aScanner(maSpans);
281 rBlockPos.miCellPos = sc::ParseBlock(rBlockPos.miCellPos, rCells, aScanner, nStart, nEnd);
284 void SingleColumnSpanSet::scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol)
286 if (!rMark.GetTableSelect(nTab))
287 // This table is not selected. Nothing to scan.
288 return;
290 ScRangeList aRanges = rMark.GetMarkedRangesForTab(nTab);
291 scan(aRanges, nTab, nCol);
294 void SingleColumnSpanSet::scan(const ScRangeList& rRanges, SCTAB nTab, SCCOL nCol)
296 for (size_t i = 0, n = rRanges.size(); i < n; ++i)
298 const ScRange & rRange = rRanges[i];
299 if (nTab < rRange.aStart.Tab() || rRange.aEnd.Tab() < nTab)
300 continue;
302 if (nCol < rRange.aStart.Col() || rRange.aEnd.Col() < nCol)
303 // This column is not in this range. Skip it.
304 continue;
306 maSpans.insert_back(rRange.aStart.Row(), rRange.aEnd.Row()+1, true);
310 void SingleColumnSpanSet::set(SCROW nRow1, SCROW nRow2, bool bVal)
312 maSpans.insert_back(nRow1, nRow2+1, bVal);
315 void SingleColumnSpanSet::getRows(std::vector<SCROW> &rRows) const
317 std::vector<SCROW> aRows;
319 SpansType aRanges;
320 getSpans(aRanges);
321 for (const auto& rRange : aRanges)
323 for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
324 aRows.push_back(nRow);
327 rRows.swap(aRows);
330 void SingleColumnSpanSet::getSpans(SpansType& rSpans) const
332 SpansType aSpans = toSpanArray<SCROW,RowSpan>(maSpans);
333 rSpans.swap(aSpans);
336 void SingleColumnSpanSet::swap( SingleColumnSpanSet& r )
338 maSpans.swap(r.maSpans);
341 bool SingleColumnSpanSet::empty() const
343 // Empty if there's only the 0..rDoc.MaxRow() span with false.
344 ColumnSpansType::const_iterator it = maSpans.begin();
345 return (it->first == 0) && !(it->second) && (++it != maSpans.end()) && (it->first == mrSheetLimits.GetMaxRowCount());
349 void RangeColumnSpanSet::executeColumnAction(ScDocument& rDoc, sc::ColumnSpanSet::ColumnAction& ac) const
351 for (SCTAB nTab = range.aStart.Tab(); nTab <= range.aEnd.Tab(); ++nTab)
353 ScTable* pTab = rDoc.FetchTable(nTab);
354 if (!pTab)
355 continue;
357 SCCOL nEndCol = pTab->ClampToAllocatedColumns(range.aEnd.Col());
358 for (SCCOL nCol = range.aStart.Col(); nCol <= nEndCol; ++nCol)
360 if (!rDoc.ValidCol(nCol))
361 break;
363 ScColumn& rColumn = pTab->aCol[nCol];
364 ac.startColumn(&rColumn);
365 ac.execute( range.aStart.Row(), range.aEnd.Row(), true );
370 } // namespace sc
372 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */