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 <memory>
21 #include <markdata.hxx>
22 #include <markarr.hxx>
23 #include <markmulti.hxx>
24 #include <rangelst.hxx>
25 #include <segmenttree.hxx>
26 #include <sheetlimits.hxx>
27 #include <document.hxx>
28 #include <columnspanset.hxx>
29 #include <fstalgorithm.hxx>
30 #include <unordered_map>
31
32 #include <osl/diagnose.h>
33
34 #include <mdds/flat_segment_tree.hpp>
35 #include <cassert>
36
37
ScMarkData(const ScSheetLimits & rSheetLimits)38 ScMarkData::ScMarkData(const ScSheetLimits& rSheetLimits) :
39 maTabMarked(),
40 aMultiSel(rSheetLimits),
41 mrSheetLimits(rSheetLimits)
42 {
43 ResetMark();
44 }
45
~ScMarkData()46 ScMarkData::~ScMarkData()
47 {
48 }
49
operator =(const ScMarkData & rOther)50 ScMarkData& ScMarkData::operator=(const ScMarkData& rOther)
51 {
52 maTabMarked = rOther.maTabMarked;
53 aMarkRange = rOther.aMarkRange;
54 aMultiRange = rOther.aMultiRange;
55 aMultiSel = rOther.aMultiSel;
56 aTopEnvelope = rOther.aTopEnvelope;
57 aBottomEnvelope = rOther.aBottomEnvelope;
58 aLeftEnvelope = rOther.aLeftEnvelope;
59 aRightEnvelope = rOther.aRightEnvelope;
60 bMarked = rOther.bMarked;
61 bMultiMarked = rOther.bMultiMarked;
62 bMarking = rOther.bMarking;
63 bMarkIsNeg = rOther.bMarkIsNeg;
64 return *this;
65 }
66
operator =(ScMarkData && rOther)67 ScMarkData& ScMarkData::operator=(ScMarkData&& rOther)
68 {
69 maTabMarked = std::move(rOther.maTabMarked);
70 aMarkRange = std::move(rOther.aMarkRange);
71 aMultiRange = std::move(rOther.aMultiRange);
72 aMultiSel = std::move(rOther.aMultiSel);
73 aTopEnvelope = std::move(rOther.aTopEnvelope);
74 aBottomEnvelope = std::move(rOther.aBottomEnvelope);
75 aLeftEnvelope = std::move(rOther.aLeftEnvelope);
76 aRightEnvelope = std::move(rOther.aRightEnvelope);
77 bMarked = rOther.bMarked;
78 bMultiMarked = rOther.bMultiMarked;
79 bMarking = rOther.bMarking;
80 bMarkIsNeg = rOther.bMarkIsNeg;
81 return *this;
82 }
83
84
ResetMark()85 void ScMarkData::ResetMark()
86 {
87 aMultiSel.Clear();
88
89 bMarked = bMultiMarked = false;
90 bMarking = bMarkIsNeg = false;
91 aTopEnvelope.RemoveAll();
92 aBottomEnvelope.RemoveAll();
93 aLeftEnvelope.RemoveAll();
94 aRightEnvelope.RemoveAll();
95 }
96
SetMarkArea(const ScRange & rRange)97 void ScMarkData::SetMarkArea( const ScRange& rRange )
98 {
99 aMarkRange = rRange;
100 aMarkRange.PutInOrder();
101 if ( !bMarked )
102 {
103 // Upon creation of a document ScFormatShell GetTextAttrState
104 // may query (default) attributes although no sheet is marked yet.
105 // => mark that one.
106 if ( !GetSelectCount() )
107 maTabMarked.insert( aMarkRange.aStart.Tab() );
108 bMarked = true;
109 }
110 }
111
GetMarkArea(ScRange & rRange) const112 void ScMarkData::GetMarkArea( ScRange& rRange ) const
113 {
114 rRange = aMarkRange; //TODO: inline ?
115 }
116
GetMultiMarkArea(ScRange & rRange) const117 void ScMarkData::GetMultiMarkArea( ScRange& rRange ) const
118 {
119 rRange = aMultiRange;
120 }
121
SetMultiMarkArea(const ScRange & rRange,bool bMark,bool bSetupMulti)122 void ScMarkData::SetMultiMarkArea( const ScRange& rRange, bool bMark, bool bSetupMulti )
123 {
124 if ( aMultiSel.IsEmpty() )
125 {
126 // if simple mark range is set, copy to multi marks
127 if ( bMarked && !bMarkIsNeg && !bSetupMulti )
128 {
129 bMarked = false;
130 SCCOL nStartCol = aMarkRange.aStart.Col();
131 SCCOL nEndCol = aMarkRange.aEnd.Col();
132 PutInOrder( nStartCol, nEndCol );
133 SetMultiMarkArea( aMarkRange, true, true );
134 }
135 }
136
137 SCCOL nStartCol = rRange.aStart.Col();
138 SCROW nStartRow = rRange.aStart.Row();
139 SCCOL nEndCol = rRange.aEnd.Col();
140 SCROW nEndRow = rRange.aEnd.Row();
141 PutInOrder( nStartRow, nEndRow );
142 PutInOrder( nStartCol, nEndCol );
143
144 aMultiSel.SetMarkArea( nStartCol, nEndCol, nStartRow, nEndRow, bMark );
145
146 if ( bMultiMarked ) // Update aMultiRange
147 {
148 if ( nStartCol < aMultiRange.aStart.Col() )
149 aMultiRange.aStart.SetCol( nStartCol );
150 if ( nStartRow < aMultiRange.aStart.Row() )
151 aMultiRange.aStart.SetRow( nStartRow );
152 if ( nEndCol > aMultiRange.aEnd.Col() )
153 aMultiRange.aEnd.SetCol( nEndCol );
154 if ( nEndRow > aMultiRange.aEnd.Row() )
155 aMultiRange.aEnd.SetRow( nEndRow );
156 }
157 else
158 {
159 aMultiRange = rRange; // new
160 bMultiMarked = true;
161 }
162 }
163
SetAreaTab(SCTAB nTab)164 void ScMarkData::SetAreaTab( SCTAB nTab )
165 {
166 aMarkRange.aStart.SetTab(nTab);
167 aMarkRange.aEnd.SetTab(nTab);
168 aMultiRange.aStart.SetTab(nTab);
169 aMultiRange.aEnd.SetTab(nTab);
170 }
171
SelectTable(SCTAB nTab,bool bNew)172 void ScMarkData::SelectTable( SCTAB nTab, bool bNew )
173 {
174 if ( bNew )
175 {
176 maTabMarked.insert( nTab );
177 }
178 else
179 {
180 maTabMarked.erase( nTab );
181 }
182 }
183
GetTableSelect(SCTAB nTab) const184 bool ScMarkData::GetTableSelect( SCTAB nTab ) const
185 {
186 return (maTabMarked.find( nTab ) != maTabMarked.end());
187 }
188
SelectOneTable(SCTAB nTab)189 void ScMarkData::SelectOneTable( SCTAB nTab )
190 {
191 maTabMarked.clear();
192 maTabMarked.insert( nTab );
193 }
194
GetSelectCount() const195 SCTAB ScMarkData::GetSelectCount() const
196 {
197 return static_cast<SCTAB> ( maTabMarked.size() );
198 }
199
GetFirstSelected() const200 SCTAB ScMarkData::GetFirstSelected() const
201 {
202 if (!maTabMarked.empty())
203 return (*maTabMarked.begin());
204
205 OSL_FAIL("GetFirstSelected: nothing selected");
206 return 0;
207 }
208
GetLastSelected() const209 SCTAB ScMarkData::GetLastSelected() const
210 {
211 if (!maTabMarked.empty())
212 return (*maTabMarked.rbegin());
213
214 OSL_FAIL("GetLastSelected: nothing selected");
215 return 0;
216 }
217
SetSelectedTabs(const MarkedTabsType & rTabs)218 void ScMarkData::SetSelectedTabs(const MarkedTabsType& rTabs)
219 {
220 MarkedTabsType aTabs(rTabs.begin(), rTabs.end());
221 maTabMarked.swap(aTabs);
222 }
223
MarkToMulti()224 void ScMarkData::MarkToMulti()
225 {
226 if ( bMarked && !bMarking )
227 {
228 SetMultiMarkArea( aMarkRange, !bMarkIsNeg );
229 bMarked = false;
230
231 // check if all multi mark ranges have been removed
232 if ( bMarkIsNeg && !HasAnyMultiMarks() )
233 ResetMark();
234 }
235 }
236
MarkToSimple()237 void ScMarkData::MarkToSimple()
238 {
239 if ( bMarking )
240 return;
241
242 if ( bMultiMarked && bMarked )
243 MarkToMulti(); // may result in bMarked and bMultiMarked reset
244
245 if ( !bMultiMarked )
246 return;
247
248 ScRange aNew = aMultiRange;
249
250 bool bOk = false;
251 SCCOL nStartCol = aNew.aStart.Col();
252 SCCOL nEndCol = aNew.aEnd.Col();
253
254 while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nStartCol ) )
255 ++nStartCol;
256 while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nEndCol ) )
257 --nEndCol;
258
259 // Rows are only taken from MarkArray
260 SCROW nStartRow, nEndRow;
261 if ( aMultiSel.HasOneMark( nStartCol, nStartRow, nEndRow ) )
262 {
263 bOk = true;
264 SCROW nCmpStart, nCmpEnd;
265 for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++)
266 if ( !aMultiSel.HasOneMark( nCol, nCmpStart, nCmpEnd )
267 || nCmpStart != nStartRow || nCmpEnd != nEndRow )
268 bOk = false;
269 }
270
271 if (bOk)
272 {
273 aNew.aStart.SetCol(nStartCol);
274 aNew.aStart.SetRow(nStartRow);
275 aNew.aEnd.SetCol(nEndCol);
276 aNew.aEnd.SetRow(nEndRow);
277
278 ResetMark();
279 aMarkRange = aNew;
280 bMarked = true;
281 bMarkIsNeg = false;
282 }
283 }
284
IsCellMarked(SCCOL nCol,SCROW nRow,bool bNoSimple) const285 bool ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, bool bNoSimple ) const
286 {
287 if ( bMarked && !bNoSimple && !bMarkIsNeg )
288 if ( aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
289 aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
290 return true;
291
292 if (bMultiMarked)
293 {
294 //TODO: test here for negative Marking ?
295
296 return aMultiSel.GetMark( nCol, nRow );
297 }
298
299 return false;
300 }
301
IsColumnMarked(SCCOL nCol) const302 bool ScMarkData::IsColumnMarked( SCCOL nCol ) const
303 {
304 // bMarkIsNeg meanwhile also for columns heads
305 //TODO: GetMarkColumnRanges for completely marked column
306
307 if ( bMarked && !bMarkIsNeg &&
308 aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
309 aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == mrSheetLimits.mnMaxRow )
310 return true;
311
312 if ( bMultiMarked && aMultiSel.IsAllMarked( nCol, 0, mrSheetLimits.mnMaxRow ) )
313 return true;
314
315 return false;
316 }
317
IsRowMarked(SCROW nRow) const318 bool ScMarkData::IsRowMarked( SCROW nRow ) const
319 {
320 // bMarkIsNeg meanwhile also for row heads
321 //TODO: GetMarkRowRanges for completely marked rows
322
323 if ( bMarked && !bMarkIsNeg &&
324 aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == mrSheetLimits.mnMaxCol &&
325 aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
326 return true;
327
328 if ( bMultiMarked )
329 return aMultiSel.IsRowMarked( nRow );
330
331 return false;
332 }
333
MarkFromRangeList(const ScRangeList & rList,bool bReset)334 void ScMarkData::MarkFromRangeList( const ScRangeList& rList, bool bReset )
335 {
336 if (bReset)
337 {
338 maTabMarked.clear();
339 ResetMark();
340 }
341
342 size_t nCount = rList.size();
343 if ( nCount == 1 && !bMarked && !bMultiMarked )
344 {
345 const ScRange& rRange = rList[ 0 ];
346 SetMarkArea( rRange );
347 SelectTable( rRange.aStart.Tab(), true );
348 }
349 else
350 {
351 for (size_t i=0; i < nCount; i++)
352 {
353 const ScRange& rRange = rList[ i ];
354 SetMultiMarkArea( rRange );
355 SelectTable( rRange.aStart.Tab(), true );
356 }
357 }
358 }
359
360 /**
361 Optimise the case of constructing from a range list, speeds up import.
362 */
ScMarkData(const ScSheetLimits & rLimits,const ScRangeList & rList)363 ScMarkData::ScMarkData(const ScSheetLimits& rLimits, const ScRangeList& rList)
364 : aMultiSel(rLimits),
365 mrSheetLimits(rLimits)
366 {
367 ResetMark();
368
369 for (const ScRange& rRange : rList)
370 maTabMarked.insert( rRange.aStart.Tab() );
371
372 if (rList.size() > 1)
373 {
374 bMultiMarked = true;
375 aMultiRange = rList.Combine();
376
377 aMultiSel.Set( rList );
378 }
379 else if (rList.size() == 1)
380 {
381 const ScRange& rRange = rList[ 0 ];
382 SetMarkArea( rRange );
383 }
384 }
385
386
FillRangeListWithMarks(ScRangeList * pList,bool bClear,SCTAB nForTab) const387 void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, bool bClear, SCTAB nForTab ) const
388 {
389 if (!pList)
390 return;
391
392 if (bClear)
393 pList->RemoveAll();
394
395 //TODO: for multiple selected tables enter multiple ranges !!!
396
397 if ( bMultiMarked )
398 {
399 SCTAB nTab = (nForTab < 0 ? aMultiRange.aStart.Tab() : nForTab);
400
401 SCCOL nStartCol = aMultiRange.aStart.Col();
402 SCCOL nEndCol = aMultiRange.aEnd.Col();
403 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
404 {
405 if (aMultiSel.HasMarks( nCol ))
406 {
407 // Feeding column-wise fragments to ScRangeList::Join() is a
408 // huge bottleneck, speed this up for multiple columns
409 // consisting of identical row sets by building a column span
410 // first. This is usually the case for filtered data, for
411 // example.
412 SCCOL nToCol = nCol+1;
413 for ( ; nToCol <= nEndCol; ++nToCol)
414 {
415 if (!aMultiSel.HasEqualRowsMarked(nCol, nToCol))
416 break;
417 }
418 --nToCol;
419 ScRange aRange( nCol, 0, nTab, nToCol, 0, nTab );
420 SCROW nTop, nBottom;
421 ScMultiSelIter aMultiIter( aMultiSel, nCol );
422 while ( aMultiIter.Next( nTop, nBottom ) )
423 {
424 aRange.aStart.SetRow( nTop );
425 aRange.aEnd.SetRow( nBottom );
426 pList->Join( aRange );
427 }
428 nCol = nToCol;
429 }
430 }
431 }
432
433 if ( bMarked )
434 {
435 if (nForTab < 0)
436 pList->push_back( aMarkRange );
437 else
438 {
439 ScRange aRange( aMarkRange );
440 aRange.aStart.SetTab( nForTab );
441 aRange.aEnd.SetTab( nForTab );
442 pList->push_back( aRange );
443 }
444 }
445 }
446
ExtendRangeListTables(ScRangeList * pList) const447 void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const
448 {
449 if (!pList)
450 return;
451
452 ScRangeList aOldList(*pList);
453 pList->RemoveAll(); //TODO: or skip the existing below
454
455 for (const auto& rTab : maTabMarked)
456 for ( size_t i=0, nCount = aOldList.size(); i<nCount; i++)
457 {
458 ScRange aRange = aOldList[ i ];
459 aRange.aStart.SetTab(rTab);
460 aRange.aEnd.SetTab(rTab);
461 pList->push_back( aRange );
462 }
463 }
464
GetMarkedRanges() const465 ScRangeList ScMarkData::GetMarkedRanges() const
466 {
467 ScRangeList aRet;
468 FillRangeListWithMarks(&aRet, false);
469 return aRet;
470 }
471
GetMarkedRangesForTab(SCTAB nTab) const472 ScRangeList ScMarkData::GetMarkedRangesForTab( SCTAB nTab ) const
473 {
474 ScRangeList aRet;
475 FillRangeListWithMarks(&aRet, false, nTab);
476 return aRet;
477 }
478
GetMarkedRowSpans() const479 std::vector<sc::ColRowSpan> ScMarkData::GetMarkedRowSpans() const
480 {
481 typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
482
483 ScRangeList aRanges = GetMarkedRanges();
484 SpansType aSpans(0, mrSheetLimits.mnMaxRow+1, false);
485 SpansType::const_iterator itPos = aSpans.begin();
486
487 for (size_t i = 0, n = aRanges.size(); i < n; ++i)
488 {
489 const ScRange& r = aRanges[i];
490 itPos = aSpans.insert(itPos, r.aStart.Row(), r.aEnd.Row()+1, true).first;
491 }
492
493 return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
494 }
495
GetMarkedColSpans() const496 std::vector<sc::ColRowSpan> ScMarkData::GetMarkedColSpans() const
497 {
498
499 if (bMultiMarked)
500 {
501 SCCOL nStartCol = aMultiRange.aStart.Col();
502 SCCOL nEndCol = aMultiRange.aEnd.Col();
503 if (bMarked)
504 {
505 // Use segment tree to merge marked with multi marked.
506 typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
507 SpansType aSpans(0, mrSheetLimits.mnMaxCol+1, false);
508 SpansType::const_iterator itPos = aSpans.begin();
509 do
510 {
511 if (aMultiSel.GetRowSelArray().HasMarks())
512 {
513 itPos = aSpans.insert(itPos, nStartCol, nEndCol+1, true).first;
514 break; // do; all columns marked
515 }
516
517 /* XXX if it turns out that span insert is too slow for lots of
518 * subsequent columns we could gather each span first and then
519 * insert. */
520 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
521 {
522 const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
523 if (pMultiArray && pMultiArray->HasMarks())
524 itPos = aSpans.insert(itPos, nCol, nCol+1, true).first;
525 }
526 }
527 while(false);
528
529 // Merge marked.
530 aSpans.insert(itPos, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col()+1, true);
531
532 return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
533 }
534 else
535 {
536 // A plain vector is sufficient, avoid segment tree and conversion
537 // to vector overhead.
538 std::vector<sc::ColRowSpan> aVec;
539 if (aMultiSel.GetRowSelArray().HasMarks())
540 {
541 aVec.emplace_back( nStartCol, nEndCol);
542 return aVec; // all columns marked
543 }
544 sc::ColRowSpan aSpan( -1, -1);
545 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
546 {
547 const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
548 if (pMultiArray && pMultiArray->HasMarks())
549 {
550 if (aSpan.mnStart == -1)
551 aSpan.mnStart = nCol;
552 aSpan.mnEnd = nCol;
553 }
554 else
555 {
556 // Add span gathered so far, if any.
557 if (aSpan.mnStart != -1)
558 {
559 aVec.push_back( aSpan);
560 aSpan.mnStart = -1;
561 }
562 }
563 }
564 // Add last span, if any.
565 if (aSpan.mnStart != -1)
566 aVec.push_back( aSpan);
567 return aVec;
568 }
569 }
570
571 // Only reached if not multi marked.
572 std::vector<sc::ColRowSpan> aVec;
573 if (bMarked)
574 {
575 aVec.emplace_back( aMarkRange.aStart.Col(), aMarkRange.aEnd.Col());
576 }
577 return aVec;
578 }
579
IsAllMarked(const ScRange & rRange) const580 bool ScMarkData::IsAllMarked( const ScRange& rRange ) const
581 {
582 if ( !bMultiMarked )
583 return false;
584
585 SCCOL nStartCol = rRange.aStart.Col();
586 SCROW nStartRow = rRange.aStart.Row();
587 SCCOL nEndCol = rRange.aEnd.Col();
588 SCROW nEndRow = rRange.aEnd.Row();
589 bool bOk = true;
590
591 if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
592 return aMultiSel.IsRowRangeMarked( nStartRow, nEndRow );
593
594 for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++)
595 if ( !aMultiSel.IsAllMarked( nCol, nStartRow, nEndRow ) )
596 bOk = false;
597
598 return bOk;
599 }
600
GetNextMarked(SCCOL nCol,SCROW nRow,bool bUp) const601 SCROW ScMarkData::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
602 {
603 if ( !bMultiMarked )
604 return nRow;
605
606 return aMultiSel.GetNextMarked( nCol, nRow, bUp );
607 }
608
HasMultiMarks(SCCOL nCol) const609 bool ScMarkData::HasMultiMarks( SCCOL nCol ) const
610 {
611 if ( !bMultiMarked )
612 return false;
613
614 return aMultiSel.HasMarks( nCol );
615 }
616
HasAnyMultiMarks() const617 bool ScMarkData::HasAnyMultiMarks() const
618 {
619 if ( !bMultiMarked )
620 return false;
621
622 return aMultiSel.HasAnyMarks();
623 }
624
InsertTab(SCTAB nTab)625 void ScMarkData::InsertTab( SCTAB nTab )
626 {
627 std::set<SCTAB> tabMarked;
628 for (const auto& rTab : maTabMarked)
629 {
630 if (rTab < nTab)
631 tabMarked.insert(rTab);
632 else
633 tabMarked.insert(rTab + 1);
634 }
635 maTabMarked.swap(tabMarked);
636 }
637
DeleteTab(SCTAB nTab)638 void ScMarkData::DeleteTab( SCTAB nTab )
639 {
640 std::set<SCTAB> tabMarked;
641 for (const auto& rTab : maTabMarked)
642 {
643 if (rTab < nTab)
644 tabMarked.insert(rTab);
645 else if (rTab > nTab)
646 tabMarked.insert(rTab - 1);
647 }
648 maTabMarked.swap(tabMarked);
649 }
650
ShiftCols(const ScDocument & rDoc,SCCOL nStartCol,sal_Int32 nColOffset)651 void ScMarkData::ShiftCols(const ScDocument& rDoc, SCCOL nStartCol, sal_Int32 nColOffset)
652 {
653 if (bMarked)
654 {
655 aMarkRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
656 }
657 else if (bMultiMarked)
658 {
659 aMultiSel.ShiftCols(nStartCol, nColOffset);
660 aMultiRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
661 }
662 }
663
ShiftRows(const ScDocument & rDoc,SCROW nStartRow,sal_Int32 nRowOffset)664 void ScMarkData::ShiftRows(const ScDocument& rDoc, SCROW nStartRow, sal_Int32 nRowOffset)
665 {
666 if (bMarked)
667 {
668 aMarkRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
669 }
670 else if (bMultiMarked)
671 {
672 aMultiSel.ShiftRows(nStartRow, nRowOffset);
673 aMultiRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
674 }
675 }
676
lcl_AddRanges(ScRange & rRangeDest,const ScRange & rNewRange)677 static void lcl_AddRanges(ScRange& rRangeDest, const ScRange& rNewRange )
678 {
679 SCCOL nStartCol = rNewRange.aStart.Col();
680 SCROW nStartRow = rNewRange.aStart.Row();
681 SCCOL nEndCol = rNewRange.aEnd.Col();
682 SCROW nEndRow = rNewRange.aEnd.Row();
683 PutInOrder( nStartRow, nEndRow );
684 PutInOrder( nStartCol, nEndCol );
685 if ( nStartCol < rRangeDest.aStart.Col() )
686 rRangeDest.aStart.SetCol( nStartCol );
687 if ( nStartRow < rRangeDest.aStart.Row() )
688 rRangeDest.aStart.SetRow( nStartRow );
689 if ( nEndCol > rRangeDest.aEnd.Col() )
690 rRangeDest.aEnd.SetCol( nEndCol );
691 if ( nEndRow > rRangeDest.aEnd.Row() )
692 rRangeDest.aEnd.SetRow( nEndRow );
693 }
694
GetSelectionCover(ScRange & rRange)695 void ScMarkData::GetSelectionCover( ScRange& rRange )
696 {
697 if( bMultiMarked )
698 {
699 rRange = aMultiRange;
700 SCCOL nStartCol = aMultiRange.aStart.Col(), nEndCol = aMultiRange.aEnd.Col();
701 PutInOrder( nStartCol, nEndCol );
702 nStartCol = ( nStartCol == 0 ) ? nStartCol : nStartCol - 1;
703 nEndCol = ( nEndCol == mrSheetLimits.mnMaxCol ) ? nEndCol : nEndCol + 1;
704 std::unique_ptr<ScFlatBoolRowSegments> pPrevColMarkedRows;
705 std::unique_ptr<ScFlatBoolRowSegments> pCurColMarkedRows;
706 std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInTopEnvelope;
707 std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInBottomEnvelope;
708 ScFlatBoolRowSegments aNoRowsMarked(mrSheetLimits.mnMaxRow);
709 aNoRowsMarked.setFalse( 0, mrSheetLimits.mnMaxRow );
710
711 bool bPrevColUnMarked = false;
712
713 for ( SCCOL nCol=nStartCol; nCol <= nEndCol; nCol++ )
714 {
715 SCROW nTop, nBottom;
716 bool bCurColUnMarked = !aMultiSel.HasMarks( nCol );
717 if ( !bCurColUnMarked )
718 {
719 pCurColMarkedRows.reset( new ScFlatBoolRowSegments(mrSheetLimits.mnMaxRow) );
720 pCurColMarkedRows->setFalse( 0, mrSheetLimits.mnMaxRow );
721 ScMultiSelIter aMultiIter( aMultiSel, nCol );
722 ScFlatBoolRowSegments::ForwardIterator aPrevItr(
723 pPrevColMarkedRows ? *pPrevColMarkedRows
724 : aNoRowsMarked); // For finding left envelope
725 ScFlatBoolRowSegments::ForwardIterator aPrevItr1(
726 pPrevColMarkedRows ? *pPrevColMarkedRows
727 : aNoRowsMarked); // For finding right envelope
728 SCROW nTopPrev = 0, nBottomPrev = 0; // For right envelope
729 while ( aMultiIter.Next( nTop, nBottom ) )
730 {
731 pCurColMarkedRows->setTrue( nTop, nBottom );
732 if( bPrevColUnMarked && ( nCol > nStartCol ))
733 {
734 ScRange aAddRange(nCol - 1, nTop, aMultiRange.aStart.Tab(),
735 nCol - 1, nBottom, aMultiRange.aStart.Tab());
736 lcl_AddRanges( rRange, aAddRange ); // Left envelope
737 aLeftEnvelope.push_back( aAddRange );
738 }
739 else if( nCol > nStartCol )
740 {
741 SCROW nTop1 = nTop, nBottom1 = nTop;
742 while( nTop1 <= nBottom && nBottom1 <= nBottom )
743 {
744 bool bRangeMarked = false;
745 const bool bHasValue = aPrevItr.getValue( nTop1, bRangeMarked );
746 assert(bHasValue); (void)bHasValue;
747 if( bRangeMarked )
748 {
749 nTop1 = aPrevItr.getLastPos() + 1;
750 nBottom1 = nTop1;
751 }
752 else
753 {
754 nBottom1 = aPrevItr.getLastPos();
755 if( nBottom1 > nBottom )
756 nBottom1 = nBottom;
757 ScRange aAddRange( nCol - 1, nTop1, aMultiRange.aStart.Tab(),
758 nCol - 1, nBottom1, aMultiRange.aStart.Tab() );
759 lcl_AddRanges( rRange, aAddRange ); // Left envelope
760 aLeftEnvelope.push_back( aAddRange );
761 nTop1 = ++nBottom1;
762 }
763 }
764 while( nTopPrev <= nBottom && nBottomPrev <= nBottom )
765 {
766 bool bRangeMarked;
767 const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
768 assert(bHasValue); (void)bHasValue;
769 if( bRangeMarked )
770 {
771 nBottomPrev = aPrevItr1.getLastPos();
772 if( nTopPrev < nTop )
773 {
774 if( nBottomPrev >= nTop )
775 {
776 nBottomPrev = nTop - 1;
777 ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
778 nCol, nBottomPrev, aMultiRange.aStart.Tab());
779 lcl_AddRanges( rRange, aAddRange ); // Right envelope
780 aRightEnvelope.push_back( aAddRange );
781 nTopPrev = nBottomPrev = (nBottom + 1);
782 }
783 else
784 {
785 ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
786 nCol, nBottomPrev, aMultiRange.aStart.Tab());
787 lcl_AddRanges( rRange, aAddRange ); // Right envelope
788 aRightEnvelope.push_back( aAddRange );
789 nTopPrev = ++nBottomPrev;
790 }
791 }
792 else
793 nTopPrev = nBottomPrev = ( nBottom + 1 );
794 }
795 else
796 {
797 nBottomPrev = aPrevItr1.getLastPos();
798 nTopPrev = ++nBottomPrev;
799 }
800 }
801 }
802 if( nTop )
803 {
804 ScRange aAddRange( nCol, nTop - 1, aMultiRange.aStart.Tab(),
805 nCol, nTop - 1, aMultiRange.aStart.Tab());
806 lcl_AddRanges( rRange, aAddRange ); // Top envelope
807 auto it = aRowToColSegmentsInTopEnvelope.find(nTop - 1);
808 if (it == aRowToColSegmentsInTopEnvelope.end())
809 it = aRowToColSegmentsInTopEnvelope.emplace(nTop - 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
810 it->second.setTrue( nCol, nCol );
811 }
812 if( nBottom < mrSheetLimits.mnMaxRow )
813 {
814 ScRange aAddRange(nCol, nBottom + 1, aMultiRange.aStart.Tab(),
815 nCol, nBottom + 1, aMultiRange.aStart.Tab());
816 lcl_AddRanges( rRange, aAddRange ); // Bottom envelope
817 auto it = aRowToColSegmentsInBottomEnvelope.find(nBottom + 1);
818 if (it == aRowToColSegmentsInBottomEnvelope.end())
819 it = aRowToColSegmentsInBottomEnvelope.emplace(nBottom + 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
820 it->second.setTrue( nCol, nCol );
821 }
822 }
823
824 while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow && ( nCol > nStartCol ) )
825 {
826 bool bRangeMarked;
827 const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
828 assert(bHasValue); (void)bHasValue;
829 if( bRangeMarked )
830 {
831 nBottomPrev = aPrevItr1.getLastPos();
832 ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
833 nCol, nBottomPrev, aMultiRange.aStart.Tab());
834 lcl_AddRanges( rRange, aAddRange ); // Right envelope
835 aRightEnvelope.push_back( aAddRange );
836 nTopPrev = ++nBottomPrev;
837 }
838 else
839 {
840 nBottomPrev = aPrevItr1.getLastPos();
841 nTopPrev = ++nBottomPrev;
842 }
843 }
844 }
845 else if( nCol > nStartCol )
846 {
847 bPrevColUnMarked = true;
848 SCROW nTopPrev = 0, nBottomPrev = 0;
849 bool bRangeMarked = false;
850 ScFlatBoolRowSegments::ForwardIterator aPrevItr(
851 pPrevColMarkedRows ? *pPrevColMarkedRows : aNoRowsMarked);
852 while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow )
853 {
854 const bool bHasValue = aPrevItr.getValue(nTopPrev, bRangeMarked);
855 assert(bHasValue); (void)bHasValue;
856 if( bRangeMarked )
857 {
858 nBottomPrev = aPrevItr.getLastPos();
859 ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
860 nCol, nBottomPrev, aMultiRange.aStart.Tab());
861 lcl_AddRanges( rRange, aAddRange ); // Right envelope
862 aRightEnvelope.push_back( aAddRange );
863 nTopPrev = ++nBottomPrev;
864 }
865 else
866 {
867 nBottomPrev = aPrevItr.getLastPos();
868 nTopPrev = ++nBottomPrev;
869 }
870 }
871 }
872 if ( bCurColUnMarked )
873 pPrevColMarkedRows.reset();
874 else
875 pPrevColMarkedRows = std::move( pCurColMarkedRows );
876 }
877 for( auto& rKV : aRowToColSegmentsInTopEnvelope )
878 {
879 SCCOL nStart = nStartCol;
880 ScFlatBoolColSegments::RangeData aRange;
881 while( nStart <= nEndCol )
882 {
883 if( !rKV.second.getRangeData( nStart, aRange ) )
884 break;
885 if( aRange.mbValue ) // is marked
886 aTopEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
887 aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
888 nStart = aRange.mnCol2 + 1;
889 }
890 }
891 for( auto& rKV : aRowToColSegmentsInBottomEnvelope )
892 {
893 SCCOL nStart = nStartCol;
894 ScFlatBoolColSegments::RangeData aRange;
895 while( nStart <= nEndCol )
896 {
897 if( !rKV.second.getRangeData( nStart, aRange ) )
898 break;
899 if( aRange.mbValue ) // is marked
900 aBottomEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
901 aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
902 nStart = aRange.mnCol2 + 1;
903 }
904 }
905 }
906 else if( bMarked )
907 {
908 aMarkRange.PutInOrder();
909 SCROW nRow1, nRow2, nRow1New, nRow2New;
910 SCCOL nCol1, nCol2, nCol1New, nCol2New;
911 SCTAB nTab1, nTab2;
912 aMarkRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
913 nCol1New = nCol1;
914 nCol2New = nCol2;
915 nRow1New = nRow1;
916 nRow2New = nRow2;
917 // Each envelope will have zero or more ranges for single rectangle selection.
918 if( nCol1 > 0 )
919 {
920 aLeftEnvelope.push_back( ScRange( nCol1 - 1, nRow1, nTab1, nCol1 - 1, nRow2, nTab2 ) );
921 --nCol1New;
922 }
923 if( nRow1 > 0 )
924 {
925 aTopEnvelope.push_back( ScRange( nCol1, nRow1 - 1, nTab1, nCol2, nRow1 - 1, nTab2 ) );
926 --nRow1New;
927 }
928 if( nCol2 < mrSheetLimits.mnMaxCol )
929 {
930 aRightEnvelope.push_back( ScRange( nCol2 + 1, nRow1, nTab1, nCol2 + 1, nRow2, nTab2 ) );
931 ++nCol2New;
932 }
933 if( nRow2 < mrSheetLimits.mnMaxRow )
934 {
935 aBottomEnvelope.push_back( ScRange( nCol1, nRow2 + 1, nTab1, nCol2, nRow2 + 1, nTab2 ) );
936 ++nRow2New;
937 }
938 rRange = ScRange( nCol1New, nRow1New, nTab1, nCol2New, nRow2New, nTab2 );
939 }
940 }
941
GetMarkArray(SCCOL nCol) const942 ScMarkArray ScMarkData::GetMarkArray( SCCOL nCol ) const
943 {
944 return aMultiSel.GetMarkArray( nCol );
945 }
946
947 //iterators
begin()948 ScMarkData::iterator ScMarkData::begin()
949 {
950 return maTabMarked.begin();
951 }
952
end()953 ScMarkData::iterator ScMarkData::end()
954 {
955 return maTabMarked.end();
956 }
957
begin() const958 ScMarkData::const_iterator ScMarkData::begin() const
959 {
960 return maTabMarked.begin();
961 }
962
end() const963 ScMarkData::const_iterator ScMarkData::end() const
964 {
965 return maTabMarked.end();
966 }
967
rbegin() const968 ScMarkData::const_reverse_iterator ScMarkData::rbegin() const
969 {
970 return maTabMarked.rbegin();
971 }
972
973 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
974