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