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