LibreOffice Module sc (master) 1
columnspanset.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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 */
9
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>
18
19#include <algorithm>
20#include <memory>
21
22#include <o3tl/safeint.hxx>
23
24namespace sc {
25
26namespace {
27
28class ColumnNonEmptyRangesScanner
29{
31 bool mbVal;
32public:
33 ColumnNonEmptyRangesScanner(ColumnSpanSet::ColumnSpansType& rRanges, bool bVal) :
34 mrRanges(rRanges), mbVal(bVal) {}
35
36 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
37 {
38 if (node.type == sc::element_type_empty)
39 return;
40
41 size_t nRow = node.position + nOffset;
42 size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
43 mrRanges.insert_back(nRow, nEndRow, mbVal);
44 }
45};
46
47}
48
49RowSpan::RowSpan(SCROW nRow1, SCROW nRow2) : mnRow1(nRow1), mnRow2(nRow2) {}
50
51ColRowSpan::ColRowSpan(SCCOLROW nStart, SCCOLROW nEnd) : mnStart(nStart), mnEnd(nEnd) {}
52
54 maSpans(nStart, nEnd+1, bInit), miPos(maSpans.begin()) {}
55
57 maSpans(rOther.maSpans), miPos(maSpans.begin()) {} // NB: copying maSpans invalidates miPos - reset it
58
61
63
65
67{
68}
69
71{
72 if (o3tl::make_unsigned(nTab) >= maTables.size())
73 maTables.resize(nTab+1);
74
75 TableType& rTab = maTables[nTab];
76 if (o3tl::make_unsigned(nCol) >= rTab.size())
77 rTab.resize(nCol+1);
78
79 if (!rTab[nCol])
80 rTab[nCol].emplace(0, rDoc.MaxRow(), /*bInit*/false);
81
82 return *rTab[nCol];
83}
84
85void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
86{
87 if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow))
88 return;
89
90 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
91 rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow, nRow+1, bVal).first;
92}
93
94void ColumnSpanSet::set(const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2, bool bVal)
95{
96 if (!ValidTab(nTab) || !rDoc.ValidCol(nCol) || !rDoc.ValidRow(nRow1) || !rDoc.ValidRow(nRow2))
97 return;
98
99 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
100 rCol.miPos = rCol.maSpans.insert(rCol.miPos, nRow1, nRow2+1, bVal).first;
101}
102
103void ColumnSpanSet::set(const ScDocument& rDoc, const ScRange& rRange, bool bVal)
104{
105 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
106 {
107 for (SCCOL nCol = rRange.aStart.Col(); nCol <= rRange.aEnd.Col(); ++nCol)
108 {
109 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
110 rCol.miPos = rCol.maSpans.insert(rCol.miPos, rRange.aStart.Row(), rRange.aEnd.Row()+1, bVal).first;
111 }
112 }
113}
114
115void ColumnSpanSet::set( const ScDocument& rDoc, SCTAB nTab, SCCOL nCol, const SingleColumnSpanSet& rSingleSet, bool bVal )
116{
118 rSingleSet.getSpans(aSpans);
119 for (const auto& rSpan : aSpans)
120 set(rDoc, nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, bVal);
121}
122
124 const ScDocument& rDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal)
125{
126 if (!rDoc.ValidColRow(nCol1, nRow1) || !rDoc.ValidColRow(nCol2, nRow2))
127 return;
128
129 if (nCol1 > nCol2 || nRow1 > nRow2)
130 return;
131
132 const ScTable* pTab = rDoc.FetchTable(nTab);
133 if (!pTab)
134 return;
135
136 nCol2 = pTab->ClampToAllocatedColumns(nCol2);
137 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
138 {
139 ColumnType& rCol = getColumn(rDoc, nTab, nCol);
140
141 const CellStoreType& rSrcCells = pTab->aCol[nCol].maCells;
142
143 if( nRow1 > pTab->aCol[nCol].GetLastDataPos())
144 continue;
145
146 ColumnNonEmptyRangesScanner aScanner(rCol.maSpans, bVal);
147 ParseBlock(rSrcCells.begin(), rSrcCells, aScanner, nRow1, nRow2);
148 rCol.miPos = rCol.maSpans.begin();
149 }
150}
151
153{
154 for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
155 {
156 if (maTables[nTab].empty())
157 continue;
158
159 ScTable* pTab = rDoc.FetchTable(nTab);
160 if (!pTab)
161 continue;
162
163 const TableType& rTab = maTables[nTab];
164 for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
165 {
166 if (!rTab[nCol])
167 continue;
168 if (nCol >= pTab->GetAllocatedColumnsCount())
169 break;
170
171 ac.startColumn(nTab, nCol);
172 const ColumnType& rCol = *rTab[nCol];
173 ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
174 SCROW nRow1, nRow2;
175 nRow1 = it->first;
176 bool bVal = it->second;
177 for (++it; it != itEnd; ++it)
178 {
179 nRow2 = it->first-1;
180 ac.execute(ScAddress(nCol, nRow1, nTab), nRow2-nRow1+1, bVal);
181
182 nRow1 = nRow2+1; // for the next iteration.
183 bVal = it->second;
184 }
185 }
186 }
187}
188
190{
191 for (size_t nTab = 0; nTab < maTables.size(); ++nTab)
192 {
193 if (maTables[nTab].empty())
194 continue;
195
196 ScTable* pTab = rDoc.FetchTable(nTab);
197 if (!pTab)
198 continue;
199
200 const TableType& rTab = maTables[nTab];
201 for (SCCOL nCol = 0; nCol < static_cast<SCCOL>(rTab.size()); ++nCol)
202 {
203 if (!rTab[nCol])
204 continue;
205 if (nCol >= pTab->GetAllocatedColumnsCount())
206 break;
207
208 ScColumn& rColumn = pTab->aCol[nCol];
209 ac.startColumn(&rColumn);
210 const ColumnType& rCol = *rTab[nCol];
211 ColumnSpansType::const_iterator it = rCol.maSpans.begin(), itEnd = rCol.maSpans.end();
212 SCROW nRow1, nRow2;
213 nRow1 = it->first;
214 bool bVal = it->second;
215 for (++it; it != itEnd; ++it)
216 {
217 nRow2 = it->first-1;
218 ac.execute(nRow1, nRow2, bVal);
219
220 nRow1 = nRow2+1; // for the next iteration.
221 bVal = it->second;
222 }
223 }
224 }
225}
226
227namespace {
228
229class NonEmptyRangesScanner
230{
232public:
233 explicit NonEmptyRangesScanner(SingleColumnSpanSet::ColumnSpansType& rRanges) : mrRanges(rRanges) {}
234
235 void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
236 {
237 if (node.type == sc::element_type_empty)
238 return;
239
240 size_t nRow = node.position + nOffset;
241 size_t nEndRow = nRow + nDataSize; // Last row of current block plus 1
242 mrRanges.insert_back(nRow, nEndRow, true);
243 }
244};
245
246}
247
249 : mrSheetLimits(rSheetLimits),
250 maSpans(0, rSheetLimits.GetMaxRowCount(), false) {}
251
253{
254 const CellStoreType& rCells = rColumn.maCells;
255 SCROW nCurRow = 0;
256 for (const auto& rCell : rCells)
257 {
258 SCROW nEndRow = nCurRow + rCell.size; // Last row of current block plus 1.
259 if (rCell.type != sc::element_type_empty)
260 maSpans.insert_back(nCurRow, nEndRow, true);
261
262 nCurRow = nEndRow;
263 }
264}
265
266void SingleColumnSpanSet::scan(const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
267{
268 if( nStart > rColumn.GetLastDataPos())
269 return;
270 const CellStoreType& rCells = rColumn.maCells;
271 NonEmptyRangesScanner aScanner(maSpans);
272 sc::ParseBlock(rCells.begin(), rCells, aScanner, nStart, nEnd);
273}
274
276 ColumnBlockConstPosition& rBlockPos, const ScColumn& rColumn, SCROW nStart, SCROW nEnd)
277{
278 if( nStart > rColumn.GetLastDataPos())
279 return;
280 const CellStoreType& rCells = rColumn.maCells;
281 NonEmptyRangesScanner aScanner(maSpans);
282 rBlockPos.miCellPos = sc::ParseBlock(rBlockPos.miCellPos, rCells, aScanner, nStart, nEnd);
283}
284
285void SingleColumnSpanSet::scan(const ScMarkData& rMark, SCTAB nTab, SCCOL nCol)
286{
287 if (!rMark.GetTableSelect(nTab))
288 // This table is not selected. Nothing to scan.
289 return;
290
291 ScRangeList aRanges = rMark.GetMarkedRangesForTab(nTab);
292 scan(aRanges, nTab, nCol);
293}
294
295void SingleColumnSpanSet::scan(const ScRangeList& rRanges, SCTAB nTab, SCCOL nCol)
296{
297 for (size_t i = 0, n = rRanges.size(); i < n; ++i)
298 {
299 const ScRange & rRange = rRanges[i];
300 if (nTab < rRange.aStart.Tab() || rRange.aEnd.Tab() < nTab)
301 continue;
302
303 if (nCol < rRange.aStart.Col() || rRange.aEnd.Col() < nCol)
304 // This column is not in this range. Skip it.
305 continue;
306
307 maSpans.insert_back(rRange.aStart.Row(), rRange.aEnd.Row()+1, true);
308 }
309}
310
311void SingleColumnSpanSet::set(SCROW nRow1, SCROW nRow2, bool bVal)
312{
313 maSpans.insert_back(nRow1, nRow2+1, bVal);
314}
315
316void SingleColumnSpanSet::getRows(std::vector<SCROW> &rRows) const
317{
318 std::vector<SCROW> aRows;
319
320 SpansType aRanges;
321 getSpans(aRanges);
322 for (const auto& rRange : aRanges)
323 {
324 for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
325 aRows.push_back(nRow);
326 }
327
328 rRows.swap(aRows);
329}
330
332{
333 SpansType aSpans = toSpanArray<SCROW,RowSpan>(maSpans);
334 rSpans.swap(aSpans);
335}
336
338{
339 maSpans.swap(r.maSpans);
340}
341
343{
344 // Empty if there's only the 0..rDoc.MaxRow() span with false.
345 ColumnSpansType::const_iterator it = maSpans.begin();
346 return (it->first == 0) && !(it->second) && (++it != maSpans.end()) && (it->first == mrSheetLimits.GetMaxRowCount());
347}
348
349
351{
352 for (SCTAB nTab = range.aStart.Tab(); nTab <= range.aEnd.Tab(); ++nTab)
353 {
354 ScTable* pTab = rDoc.FetchTable(nTab);
355 if (!pTab)
356 continue;
357
358 SCCOL nEndCol = pTab->ClampToAllocatedColumns(range.aEnd.Col());
359 for (SCCOL nCol = range.aStart.Col(); nCol <= nEndCol; ++nCol)
360 {
361 if (!rDoc.ValidCol(nCol))
362 break;
363
364 ScColumn& rColumn = pTab->aCol[nCol];
365 ac.startColumn(&rColumn);
366 ac.execute( range.aStart.Row(), range.aEnd.Row(), true );
367 }
368 }
369}
370
371} // namespace sc
372
373/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool ValidTab(SCTAB nTab)
Definition: address.hxx:111
size_t mnEnd
Definition: cellvalues.cxx:25
size_t mnStart
Definition: cellvalues.cxx:24
SCTAB Tab() const
Definition: address.hxx:283
SCROW Row() const
Definition: address.hxx:274
SCCOL Col() const
Definition: address.hxx:279
sc::CellStoreType maCells
Definition: column.hxx:197
SCROW GetLastDataPos() const
Definition: column2.cxx:1417
bool ValidRow(SCROW nRow) const
Definition: document.hxx:900
SC_DLLPUBLIC ScTable * FetchTable(SCTAB nTab)
Definition: document.cxx:2509
SC_DLLPUBLIC SCROW MaxRow() const
Definition: document.hxx:893
bool ValidCol(SCCOL nCol) const
Definition: document.hxx:899
bool ValidColRow(SCCOL nCol, SCROW nRow) const
Definition: document.hxx:901
todo: It should be possible to have MarkArrays for each table, in order to enable "search all" across...
Definition: markdata.hxx:43
ScRangeList GetMarkedRangesForTab(SCTAB nTab) const
Get marked ranges with sheet-tab set to nTab.
Definition: markdata.cxx:457
bool GetTableSelect(SCTAB nTab) const
Definition: markdata.cxx:169
size_t size() const
Definition: rangelst.hxx:89
ScAddress aEnd
Definition: address.hxx:498
ScAddress aStart
Definition: address.hxx:497
ScColContainer aCol
Definition: table.hxx:162
SCCOL GetAllocatedColumnsCount() const
Definition: table.hxx:1164
SCCOL ClampToAllocatedColumns(SCCOL nCol) const
Definition: table.hxx:1163
virtual void startColumn(SCTAB nTab, SCCOL nCol)
virtual void execute(const ScAddress &rPos, SCROW nLength, bool bVal)=0
virtual void execute(SCROW nRow1, SCROW nRow2, bool bVal)=0
virtual void startColumn(ScColumn *pCol)=0
mdds::flat_segment_tree< SCROW, bool > ColumnSpansType
void scan(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bVal)
Scan specified range in a specified sheet and mark all non-empty cells with specified boolean value.
ColumnType & getColumn(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol)
std::vector< std::optional< ColumnType > > TableType
std::vector< TableType > maTables
void executeAction(ScDocument &rDoc, Action &ac) const
void executeColumnAction(ScDocument &rDoc, ColumnAction &ac) const
void set(const ScDocument &rDoc, SCTAB nTab, SCCOL nCol, SCROW nRow, bool bVal)
void executeColumnAction(ScDocument &rDoc, sc::ColumnSpanSet::ColumnAction &ac) const
Keep track of spans in a single column only.
void scan(const ScColumn &rColumn)
Scan an entire column and tag all non-empty cell positions.
ScSheetLimits const & mrSheetLimits
SingleColumnSpanSet(ScSheetLimits const &)
void swap(SingleColumnSpanSet &r)
std::vector< RowSpan > SpansType
void getSpans(SpansType &rSpans) const
void getRows(std::vector< SCROW > &rRows) const
bool empty() const
Whether there isn't any row tagged.
void set(SCROW nRow1, SCROW nRow2, bool bVal)
mdds::flat_segment_tree< SCROW, bool > ColumnSpansType
sal_Int64 n
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
enumrange< T >::Iterator begin(enumrange< T >)
CAUTION! The following defines must be in the same namespace as the respective type.
Definition: broadcast.cxx:15
StoreT::const_iterator ParseBlock(const typename StoreT::const_iterator &itPos, const StoreT &rStore, Func &rFunc, typename StoreT::size_type nStart, typename StoreT::size_type nEnd)
Generic algorithm to parse blocks of multi_type_vector either partially or fully.
mdds::mtv::soa::multi_type_vector< CellStoreTraits > CellStoreType
Cell container.
const mdds::mtv::element_t element_type_empty
Definition: mtvelements.hxx:56
SCROW GetMaxRowCount() const
Definition: sheetlimits.hxx:66
ColRowSpan(SCCOLROW nStart, SCCOLROW nEnd)
CellStoreType::const_iterator miCellPos
ColumnType(SCROW nStart, SCROW nEnd, bool bInit)
ColumnSpansType::const_iterator miPos
RowSpan(SCROW nRow1, SCROW nRow2)
sal_Int32 SCCOLROW
a type capable of holding either SCCOL or SCROW
Definition: types.hxx:23
sal_Int16 SCTAB
Definition: types.hxx:22
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17