1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
10 #include <columnspanset.hxx>
13 #include <document.hxx>
14 #include <mtvfunctions.hxx>
15 #include <markdata.hxx>
16 #include <rangelst.hxx>
17 #include <fstalgorithm.hxx>
21 #include <o3tl/safeint.hxx>
27 class ColumnNonEmptyRangesScanner
29 ColumnSpanSet::ColumnSpansType
& mrRanges
;
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
)
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())
79 rTab
[nCol
].emplace(0, rDoc
.MaxRow(), /*bInit*/false);
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
))
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
))
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
))
128 if (nCol1
> nCol2
|| nRow1
> nRow2
)
131 const ScTable
* pTab
= rDoc
.FetchTable(nTab
);
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())
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())
158 ScTable
* pTab
= rDoc
.FetchTable(nTab
);
162 const TableType
& rTab
= maTables
[nTab
];
163 for (SCCOL nCol
= 0; nCol
< static_cast<SCCOL
>(rTab
.size()); ++nCol
)
167 if (nCol
>= pTab
->GetAllocatedColumnsCount())
170 ac
.startColumn(nTab
, nCol
);
171 const ColumnType
& rCol
= *rTab
[nCol
];
172 ColumnSpansType::const_iterator it
= rCol
.maSpans
.begin(), itEnd
= rCol
.maSpans
.end();
175 bool bVal
= it
->second
;
176 for (++it
; it
!= itEnd
; ++it
)
179 ac
.execute(ScAddress(nCol
, nRow1
, nTab
), nRow2
-nRow1
+1, bVal
);
181 nRow1
= nRow2
+1; // for the next iteration.
188 void ColumnSpanSet::executeColumnAction(ScDocument
& rDoc
, ColumnAction
& ac
) const
190 for (size_t nTab
= 0; nTab
< maTables
.size(); ++nTab
)
192 if (maTables
[nTab
].empty())
195 ScTable
* pTab
= rDoc
.FetchTable(nTab
);
199 const TableType
& rTab
= maTables
[nTab
];
200 for (SCCOL nCol
= 0; nCol
< static_cast<SCCOL
>(rTab
.size()); ++nCol
)
204 if (nCol
>= pTab
->GetAllocatedColumnsCount())
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();
213 bool bVal
= it
->second
;
214 for (++it
; it
!= itEnd
; ++it
)
217 ac
.execute(nRow1
, nRow2
, bVal
);
219 nRow1
= nRow2
+1; // for the next iteration.
228 class NonEmptyRangesScanner
230 SingleColumnSpanSet::ColumnSpansType
& mrRanges
;
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
)
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
;
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);
265 void SingleColumnSpanSet::scan(const ScColumn
& rColumn
, SCROW nStart
, SCROW nEnd
)
267 if( nStart
> rColumn
.GetLastDataPos())
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())
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.
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
)
302 if (nCol
< rRange
.aStart
.Col() || rRange
.aEnd
.Col() < nCol
)
303 // This column is not in this range. Skip it.
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
;
321 for (const auto& rRange
: aRanges
)
323 for (SCROW nRow
= rRange
.mnRow1
; nRow
<= rRange
.mnRow2
; ++nRow
)
324 aRows
.push_back(nRow
);
330 void SingleColumnSpanSet::getSpans(SpansType
& rSpans
) const
332 SpansType aSpans
= toSpanArray
<SCROW
,RowSpan
>(maSpans
);
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
);
357 SCCOL nEndCol
= pTab
->ClampToAllocatedColumns(range
.aEnd
.Col());
358 for (SCCOL nCol
= range
.aStart
.Col(); nCol
<= nEndCol
; ++nCol
)
360 if (!rDoc
.ValidCol(nCol
))
363 ScColumn
& rColumn
= pTab
->aCol
[nCol
];
364 ac
.startColumn(&rColumn
);
365 ac
.execute( range
.aStart
.Row(), range
.aEnd
.Row(), true );
372 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */