LibreOffice Module sc (master) 1
markmulti.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 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
20#include <markmulti.hxx>
21#include <markarr.hxx>
22#include <rangelst.hxx>
23#include <segmenttree.hxx>
24#include <sheetlimits.hxx>
25
26#include <o3tl/safeint.hxx>
27
28#include <algorithm>
29
31 : aRowSel(rSheetLimits), mrSheetLimits(rSheetLimits)
32{
33}
34
36{
38 aRowSel = rOther.aRowSel;
39 return *this;
40}
41
43{
44 aMultiSelContainer = std::move(rOther.aMultiSelContainer);
45 aRowSel = std::move(rOther.aRowSel);
46 return *this;
47}
48
49
51{
52 aMultiSelContainer.clear();
53 aRowSel.Reset();
54}
55
57{
58 SCCOL nCount = 0;
59 for (const auto & i : aMultiSelContainer)
60 if (i.HasMarks())
61 ++nCount;
62 return nCount;
63}
64
65bool ScMultiSel::HasMarks( SCCOL nCol ) const
66{
67 if ( aRowSel.HasMarks() )
68 return true;
69 return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();
70}
71
72bool ScMultiSel::HasOneMark( SCCOL nCol, SCROW& rStartRow, SCROW& rEndRow ) const
73{
74 SCROW nRow1 = -1, nRow2 = -1, nRow3 = -1, nRow4 = -1;
75 bool aResult1 = aRowSel.HasOneMark( nRow1, nRow2 );
76 bool aResult2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size())
77 && aMultiSelContainer[nCol].HasOneMark( nRow3, nRow4 );
78
79 if ( aResult1 || aResult2 )
80 {
81 if ( aResult1 && aResult2 )
82 {
83 if ( ( nRow2 + 1 ) < nRow3 )
84 return false;
85 if ( ( nRow4 + 1 ) < nRow1 )
86 return false;
87
88 auto aRows = std::minmax( { nRow1, nRow2, nRow3, nRow4 } );
89 rStartRow = aRows.first;
90 rEndRow = aRows.second;
91 return true;
92 }
93 if ( aResult1 )
94 {
95 rStartRow = nRow1;
96 rEndRow = nRow2;
97 return true;
98 }
99
100 rStartRow = nRow3;
101 rEndRow = nRow4;
102 return true;
103 }
104
105 return false;
106}
107
108bool ScMultiSel::GetMark( SCCOL nCol, SCROW nRow ) const
109{
110 if ( aRowSel.GetMark( nRow ) )
111 return true;
112 return nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].GetMark(nRow);
113}
114
115bool ScMultiSel::IsAllMarked( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const
116{
117 bool bHasMarks1 = aRowSel.HasMarks();
118 bool bHasMarks2 = nCol < static_cast<SCCOL>(aMultiSelContainer.size()) && aMultiSelContainer[nCol].HasMarks();
119
120 if ( !bHasMarks1 && !bHasMarks2 )
121 return false;
122
123 if ( bHasMarks1 && bHasMarks2 )
124 {
125 if ( aRowSel.IsAllMarked( nStartRow, nEndRow ) ||
126 aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow ) )
127 return true;
128 ScMultiSelIter aMultiIter( *this, nCol );
130 bool bRet = aMultiIter.GetRangeData( nStartRow, aRowRange );
131 return bRet && aRowRange.mbValue && aRowRange.mnRow2 >= nEndRow;
132 }
133
134 if ( bHasMarks1 )
135 return aRowSel.IsAllMarked( nStartRow, nEndRow );
136
137 return aMultiSelContainer[nCol].IsAllMarked( nStartRow, nEndRow );
138}
139
141{
142 bool bCol1Exists = nCol1 < static_cast<SCCOL>(aMultiSelContainer.size());
143 bool bCol2Exists = nCol2 < static_cast<SCCOL>(aMultiSelContainer.size());
144 if ( bCol1Exists || bCol2Exists )
145 {
146 if ( bCol1Exists && bCol2Exists )
147 return aMultiSelContainer[nCol1] == aMultiSelContainer[nCol2];
148 else if ( bCol1Exists )
149 return !aMultiSelContainer[nCol1].HasMarks();
150 else
151 return !aMultiSelContainer[nCol2].HasMarks();
152 }
153
154 return true;
155}
156
158{
159 if( nMinCol > nLastCol )
160 return nMinCol;
161 if( nLastCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
162 {
163 if( nMinCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
164 return nMinCol;
165 SCCOL nCol = static_cast<SCCOL>(aMultiSelContainer.size()) - 1;
166 while( nCol >= nMinCol && aMultiSelContainer[nCol] == aRowSel )
167 --nCol;
168 return nCol + 1;
169 }
170 SCCOL nCol = nLastCol - 1;
171 while( nCol >= nMinCol && aMultiSelContainer[nCol] == aMultiSelContainer[nLastCol] )
172 --nCol;
173 return nCol + 1;
174}
175
176SCROW ScMultiSel::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
177{
178 if ( nCol >= static_cast<SCCOL>(aMultiSelContainer.size()) || !aMultiSelContainer[nCol].HasMarks() )
179 return aRowSel.GetNextMarked( nRow, bUp );
180
181 SCROW nRow1, nRow2;
182 nRow1 = aRowSel.GetNextMarked( nRow, bUp );
183 nRow2 = aMultiSelContainer[nCol].GetNextMarked( nRow, bUp );
184 if ( nRow1 == nRow2 )
185 return nRow1;
186 if ( nRow1 == -1 )
187 return nRow2;
188 if ( nRow2 == -1 )
189 return nRow1;
190
191 PutInOrder( nRow1, nRow2 );
192 return ( bUp ? nRow2 : nRow1 );
193}
194
195void ScMultiSel::MarkAllCols( SCROW nStartRow, SCROW nEndRow )
196{
198 for ( SCCOL nCol = mrSheetLimits.mnMaxCol; nCol >= 0; --nCol )
199 {
200 aMultiSelContainer[nCol].SetMarkArea( nStartRow, nEndRow, true );
201 }
202}
203
204void ScMultiSel::SetMarkArea( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow, bool bMark )
205{
206 if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
207 {
208 aRowSel.SetMarkArea( nStartRow, nEndRow, bMark );
209 if ( !bMark )
210 {
211 // Remove any per column marks for the row range.
212 for ( auto& aIter : aMultiSelContainer )
213 if ( aIter.HasMarks() )
214 aIter.SetMarkArea( nStartRow, nEndRow, false );
215 }
216 return;
217 }
218
219 // Bad case - we need to extend aMultiSelContainer size to MAXCOL
220 // and move row marks from aRowSel to aMultiSelContainer
221 if ( !bMark && aRowSel.HasMarks() )
222 {
223 SCROW nBeg, nLast = nEndRow;
224 if ( aRowSel.GetMark( nStartRow ) )
225 {
226 nBeg = nStartRow;
227 nLast = aRowSel.GetMarkEnd( nStartRow, false );
228 }
229 else
230 {
231 nBeg = aRowSel.GetNextMarked( nStartRow, false );
232 if ( nBeg != mrSheetLimits.GetMaxRowCount() )
233 nLast = aRowSel.GetMarkEnd( nBeg, false );
234 }
235
236 if ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast >= nEndRow && nBeg <= nEndRow )
237 MarkAllCols( nBeg, nEndRow );
238 else
239 {
240 while ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast < nEndRow )
241 {
242 MarkAllCols( nBeg, nLast );
243 nBeg = aRowSel.GetNextMarked( nLast + 1, false );
244 if ( nBeg != mrSheetLimits.GetMaxRowCount() )
245 nLast = aRowSel.GetMarkEnd( nBeg, false );
246 }
247 if ( nBeg != mrSheetLimits.GetMaxRowCount() && nLast >= nEndRow && nBeg <= nEndRow )
248 MarkAllCols( nBeg, nEndRow );
249 }
250
251 aRowSel.SetMarkArea( nStartRow, nEndRow, false );
252 }
253
254 if (nEndCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
255 aMultiSelContainer.resize(nEndCol+1, ScMarkArray(mrSheetLimits));
256 for ( SCCOL nColIter = nEndCol; nColIter >= nStartCol; --nColIter )
257 aMultiSelContainer[nColIter].SetMarkArea( nStartRow, nEndRow, bMark );
258}
259
264void ScMultiSel::Set( ScRangeList const & rList )
265{
266 Clear();
267 if (rList.size() == 0)
268 return;
269
270 // sort by row to make the combining/merging faster
271 auto aNewList = rList;
272 std::sort(aNewList.begin(), aNewList.end(),
273 [](const ScRange& lhs, const ScRange& rhs)
274 {
275 return lhs.aStart.Row() < rhs.aStart.Row();
276 });
277
278 std::vector<std::vector<ScMarkEntry>> aMarkEntriesPerCol(mrSheetLimits.mnMaxCol+1);
279
280 SCCOL nMaxCol = -1;
281 for (const ScRange& rRange : aNewList)
282 {
283 SCCOL nStartCol = rRange.aStart.Col();
284 SCROW nStartRow = rRange.aStart.Row();
285 SCCOL nEndCol = rRange.aEnd.Col();
286 SCROW nEndRow = rRange.aEnd.Row();
287 assert( nEndRow >= nStartRow && "this method assumes the input data has ranges with endrow>=startrow");
288 assert( nEndCol >= nStartCol && "this method assumes the input data has ranges with endcol>=startcol");
289 if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
290 aRowSel.SetMarkArea( nStartRow, nEndRow, /*bMark*/true );
291 else
292 {
293 for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol )
294 {
295 auto & rMarkEntries = aMarkEntriesPerCol[nCol];
296 int nEntries = rMarkEntries.size();
297 if (nEntries > 1 && nStartRow >= rMarkEntries[nEntries-2].nRow+1
298 && nStartRow <= rMarkEntries[nEntries-1].nRow+1)
299 {
300 // overlaps or directly adjacent previous range
301 rMarkEntries.back().nRow = std::max(nEndRow, rMarkEntries.back().nRow);
302 }
303 else
304 {
305 // new range
306 if (nStartRow > 0)
307 rMarkEntries.emplace_back(ScMarkEntry{nStartRow-1, false});
308 rMarkEntries.emplace_back(ScMarkEntry{nEndRow, true});
309 }
310 }
311 nMaxCol = std::max(nMaxCol, nEndCol);
312 }
313 }
314
315 aMultiSelContainer.resize(nMaxCol+1, ScMarkArray(mrSheetLimits));
316 for (SCCOL nCol = 0; nCol<=nMaxCol; ++nCol)
317 if (!aMarkEntriesPerCol[nCol].empty())
318 aMultiSelContainer[nCol].Set( std::move(aMarkEntriesPerCol[nCol]) );
319}
320
322{
323 return aRowSel.GetMark( nRow );
324}
325
326bool ScMultiSel::IsRowRangeMarked( SCROW nStartRow, SCROW nEndRow ) const
327{
328 if ( !aRowSel.GetMark( nStartRow ) )
329 return false;
330 SCROW nLast = aRowSel.GetMarkEnd( nStartRow, false );
331 return ( nLast >= nEndRow );
332}
333
335{
336 ScMultiSelIter aMultiIter( *this, nCol );
337 ScMarkArray aMarkArray(mrSheetLimits);
338 SCROW nTop, nBottom;
339 while( aMultiIter.Next( nTop, nBottom ) )
340 aMarkArray.SetMarkArea( nTop, nBottom, true );
341 return aMarkArray;
342}
343
345{
346 if ( aRowSel.HasMarks() )
347 return true;
348 for ( const auto& aPair : aMultiSelContainer )
349 if ( aPair.HasMarks() )
350 return true;
351 return false;
352}
353
354void ScMultiSel::ShiftCols(SCCOL nStartCol, sal_Int32 nColOffset)
355{
356 if (nStartCol > mrSheetLimits.mnMaxCol)
357 return;
358
359 ScMultiSel aNewMultiSel(*this);
360 Clear();
361
362 if (nColOffset < 0)
363 {
364 // columns that would be moved on the left of nStartCol must be removed
365 const SCCOL nEndPos = std::min<SCCOL>(aNewMultiSel.aMultiSelContainer.size(), nStartCol - nColOffset);
366 for (SCCOL nSearchPos = nStartCol; nSearchPos < nEndPos; ++nSearchPos)
367 aNewMultiSel.aMultiSelContainer[nSearchPos].Reset();
368 }
369
370 SCCOL nCol = 0;
371 for (const auto& aSourceArray : aNewMultiSel.aMultiSelContainer)
372 {
373 SCCOL nDestCol = nCol;
374 if (nDestCol >= nStartCol)
375 {
376 nDestCol += nColOffset;
377 if (nDestCol < 0)
378 nDestCol = 0;
379 else if (nDestCol > mrSheetLimits.mnMaxCol)
380 nDestCol = mrSheetLimits.mnMaxCol;
381 }
382 if (nDestCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
384 aMultiSelContainer[nDestCol] = aSourceArray;
385 ++nCol;
386 }
387 aRowSel = aNewMultiSel.aRowSel;
388
389 if (!(nColOffset > 0 && nStartCol > 0 && o3tl::make_unsigned(nStartCol) < aNewMultiSel.aMultiSelContainer.size()))
390 return;
391
392 // insert nColOffset new columns, and select their cells if they are selected
393 // both in the old column at nStartPos and in the previous column
394 auto& rPrevPos = aNewMultiSel.aMultiSelContainer[nStartCol - 1];
395 auto& rStartPos = aNewMultiSel.aMultiSelContainer[nStartCol];
396 auto& rNewCol = aMultiSelContainer[nStartCol];
397 rNewCol = rStartPos;
398 rNewCol.Intersect(rPrevPos);
399 if (nStartCol + nColOffset >= static_cast<SCCOL>(aNewMultiSel.aMultiSelContainer.size()))
400 aNewMultiSel.aMultiSelContainer.resize(nStartCol + nColOffset, ScMarkArray(mrSheetLimits));
401 for (tools::Long i = 1; i < nColOffset; ++i)
402 aMultiSelContainer[nStartCol + i] = rNewCol;
403}
404
405void ScMultiSel::ShiftRows(SCROW nStartRow, sal_Int32 nRowOffset)
406{
407 for (auto& aPair: aMultiSelContainer)
408 aPair.Shift(nStartRow, nRowOffset);
409 aRowSel.Shift(nStartRow, nRowOffset);
410}
411
413{
414 if (nCol >= static_cast<SCCOL>(aMultiSelContainer.size()))
415 return nullptr;
416 return &aMultiSelContainer[nCol];
417}
418
420 aMarkArrayIter(nullptr),
421 nNextSegmentStart(0)
422{
423 bool bHasMarks1 = rMultiSel.aRowSel.HasMarks();
424 bool bHasMarks2 = nCol < static_cast<SCCOL>(rMultiSel.aMultiSelContainer.size())
425 && rMultiSel.aMultiSelContainer[nCol].HasMarks();
426
427 if (bHasMarks1 && bHasMarks2)
428 {
430 pRowSegs->setFalse( 0, rMultiSel.mrSheetLimits.mnMaxRow );
431 {
432 ScMarkArrayIter aMarkIter( &rMultiSel.aRowSel );
433 SCROW nTop, nBottom;
434 while ( aMarkIter.Next( nTop, nBottom ) )
435 pRowSegs->setTrue( nTop, nBottom );
436 }
437
438 {
439 ScMarkArrayIter aMarkIter( &rMultiSel.aMultiSelContainer[nCol] );
440 SCROW nTop, nBottom;
441 while ( aMarkIter.Next( nTop, nBottom ) )
442 pRowSegs->setTrue( nTop, nBottom );
443 }
444 }
445 else if (bHasMarks1)
446 {
447 aMarkArrayIter.reset( &rMultiSel.aRowSel);
448 }
449 else if (bHasMarks2)
450 {
451 aMarkArrayIter.reset( &rMultiSel.aMultiSelContainer[nCol]);
452 }
453}
454
455bool ScMultiSelIter::Next( SCROW& rTop, SCROW& rBottom )
456{
457 if (pRowSegs)
458 {
460 bool bRet = pRowSegs->getRangeData( nNextSegmentStart, aRowRange );
461 if ( bRet && !aRowRange.mbValue )
462 {
463 nNextSegmentStart = aRowRange.mnRow2 + 1;
464 bRet = pRowSegs->getRangeData( nNextSegmentStart, aRowRange );
465 }
466 if ( bRet )
467 {
468 rTop = aRowRange.mnRow1;
469 rBottom = aRowRange.mnRow2;
470 nNextSegmentStart = rBottom + 1;
471 }
472 return bRet;
473 }
474
475 return aMarkArrayIter.Next( rTop, rBottom);
476}
477
479{
480 assert(pRowSegs);
481 return pRowSegs->getRangeData( nRow, rRowRange);
482}
483
484
485/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void PutInOrder(T &nStart, T &nEnd)
Definition: address.hxx:150
bool Next(SCROW &rTop, SCROW &rBottom)
Definition: markarr.cxx:441
void reset(const ScMarkArray *pNewArray)
Definition: markarr.cxx:435
This is a rather odd datastructure.
Definition: markarr.hxx:44
SCROW GetNextMarked(SCROW nRow, bool bUp) const
Including current row, may return -1 if bUp and not found.
Definition: markarr.cxx:286
bool IsAllMarked(SCROW nStartRow, SCROW nEndRow) const
Definition: markarr.cxx:217
void Shift(SCROW nStartRow, tools::Long nOffset)
Definition: markarr.cxx:328
SCROW GetMarkEnd(SCROW nRow, bool bUp) const
Definition: markarr.cxx:309
bool HasMarks() const
Definition: markarr.hxx:62
void Reset(bool bMarked=false, SCSIZE nNeeded=1)
Definition: markarr.cxx:45
void SetMarkArea(SCROW nStartRow, SCROW nEndRow, bool bMarked)
Definition: markarr.cxx:101
bool GetMark(SCROW nRow) const
Definition: markarr.cxx:91
bool HasOneMark(SCROW &rStartRow, SCROW &rEndRow) const
Definition: markarr.cxx:231
bool GetRangeData(SCROW nRow, ScFlatBoolRowSegments::RangeData &rRowRange) const
Only to be used by ScMultiSel::IsAllMarked() or otherwise sure that a segment tree is actually used.
Definition: markmulti.cxx:478
std::unique_ptr< ScFlatBoolRowSegments > pRowSegs
Definition: markmulti.hxx:80
ScMarkArrayIter aMarkArrayIter
Definition: markmulti.hxx:81
bool Next(SCROW &rTop, SCROW &rBottom)
Definition: markmulti.cxx:455
ScMultiSelIter(const ScMultiSel &rMultiSel, SCCOL nCol)
Definition: markmulti.cxx:419
SCROW nNextSegmentStart
Definition: markmulti.hxx:82
bool GetMark(SCCOL nCol, SCROW nRow) const
Definition: markmulti.cxx:108
bool HasOneMark(SCCOL nCol, SCROW &rStartRow, SCROW &rEndRow) const
Definition: markmulti.cxx:72
bool IsAllMarked(SCCOL nCol, SCROW nStartRow, SCROW nEndRow) const
Definition: markmulti.cxx:115
ScMultiSel & operator=(const ScMultiSel &rMultiSel)
Definition: markmulti.cxx:35
const ScSheetLimits & mrSheetLimits
Definition: markmulti.hxx:36
ScMarkArray GetMarkArray(SCCOL nCol) const
Definition: markmulti.cxx:334
bool IsRowMarked(SCROW nRow) const
Definition: markmulti.cxx:321
bool HasAnyMarks() const
Definition: markmulti.cxx:344
const ScMarkArray * GetMultiSelArray(SCCOL nCol) const
Definition: markmulti.cxx:412
std::vector< ScMarkArray > aMultiSelContainer
Definition: markmulti.hxx:34
SCCOL GetStartOfEqualColumns(SCCOL nLastCol, SCCOL nMinCol=0) const
Definition: markmulti.cxx:157
ScMarkArray aRowSel
Definition: markmulti.hxx:35
void ShiftRows(SCROW nStartRow, sal_Int32 nRowOffset)
Definition: markmulti.cxx:405
SCCOL GetMultiSelectionCount() const
Definition: markmulti.cxx:56
ScMultiSel(ScSheetLimits const &)
Definition: markmulti.cxx:30
void Set(ScRangeList const &)
optimised init-from-range-list.
Definition: markmulti.cxx:264
void MarkAllCols(SCROW nStartRow, SCROW nEndRow)
Definition: markmulti.cxx:195
void ShiftCols(SCCOL nStartCol, sal_Int32 nColOffset)
Definition: markmulti.cxx:354
void Clear()
Definition: markmulti.cxx:50
bool HasMarks(SCCOL nCol) const
Definition: markmulti.cxx:65
bool HasEqualRowsMarked(SCCOL nCol1, SCCOL nCol2) const
Definition: markmulti.cxx:140
bool IsRowRangeMarked(SCROW nStartRow, SCROW nEndRow) const
Definition: markmulti.cxx:326
void SetMarkArea(SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow, bool bMark)
Definition: markmulti.cxx:204
SCROW GetNextMarked(SCCOL nCol, SCROW nRow, bool bUp) const
Definition: markmulti.cxx:176
size_t size() const
Definition: rangelst.hxx:89
int nCount
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
long Long
const SCCOL mnMaxCol
Definition: sheetlimits.hxx:29
SCROW GetMaxRowCount() const
Definition: sheetlimits.hxx:66
const SCROW mnMaxRow
Maximum addressable column.
Definition: sheetlimits.hxx:30
sal_Int16 SCCOL
Definition: types.hxx:21
sal_Int32 SCROW
Definition: types.hxx:17