tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / markdata.cxx
blob98a3aebe5c8b003e19412220290ddca7d266fc2b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
32 #include <osl/diagnose.h>
34 #include <mdds/flat_segment_tree.hpp>
35 #include <cassert>
38 ScMarkData::ScMarkData(const ScSheetLimits& rSheetLimits) :
39 aMultiSel(rSheetLimits),
40 mrSheetLimits(rSheetLimits)
42 ResetMark();
45 ScMarkData& ScMarkData::operator=(const ScMarkData& rOther)
47 maTabMarked = rOther.maTabMarked;
48 aMarkRange = rOther.aMarkRange;
49 aMultiRange = rOther.aMultiRange;
50 aMultiSel = rOther.aMultiSel;
51 aTopEnvelope = rOther.aTopEnvelope;
52 aBottomEnvelope = rOther.aBottomEnvelope;
53 aLeftEnvelope = rOther.aLeftEnvelope;
54 aRightEnvelope = rOther.aRightEnvelope;
55 bMarked = rOther.bMarked;
56 bMultiMarked = rOther.bMultiMarked;
57 bMarking = rOther.bMarking;
58 bMarkIsNeg = rOther.bMarkIsNeg;
59 return *this;
62 ScMarkData& ScMarkData::operator=(ScMarkData&& rOther)
64 maTabMarked = std::move(rOther.maTabMarked);
65 aMarkRange = std::move(rOther.aMarkRange);
66 aMultiRange = std::move(rOther.aMultiRange);
67 aMultiSel = std::move(rOther.aMultiSel);
68 aTopEnvelope = std::move(rOther.aTopEnvelope);
69 aBottomEnvelope = std::move(rOther.aBottomEnvelope);
70 aLeftEnvelope = std::move(rOther.aLeftEnvelope);
71 aRightEnvelope = std::move(rOther.aRightEnvelope);
72 bMarked = rOther.bMarked;
73 bMultiMarked = rOther.bMultiMarked;
74 bMarking = rOther.bMarking;
75 bMarkIsNeg = rOther.bMarkIsNeg;
76 return *this;
80 void ScMarkData::ResetMark()
82 aMultiSel.Clear();
84 bMarked = bMultiMarked = false;
85 bMarking = bMarkIsNeg = false;
86 aTopEnvelope.RemoveAll();
87 aBottomEnvelope.RemoveAll();
88 aLeftEnvelope.RemoveAll();
89 aRightEnvelope.RemoveAll();
92 void ScMarkData::SetMarkArea( const ScRange& rRange )
94 aMarkRange = rRange;
95 aMarkRange.PutInOrder();
96 if ( !bMarked )
98 // Upon creation of a document ScFormatShell GetTextAttrState
99 // may query (default) attributes although no sheet is marked yet.
100 // => mark that one.
101 if ( !GetSelectCount() )
102 maTabMarked.insert( aMarkRange.aStart.Tab() );
103 bMarked = true;
107 void ScMarkData::SetMultiMarkArea( const ScRange& rRange, bool bMark, bool bSetupMulti )
109 if ( aMultiSel.IsEmpty() )
111 // if simple mark range is set, copy to multi marks
112 if ( bMarked && !bMarkIsNeg && !bSetupMulti )
114 bMarked = false;
115 SCCOL nStartCol = aMarkRange.aStart.Col();
116 SCCOL nEndCol = aMarkRange.aEnd.Col();
117 PutInOrder( nStartCol, nEndCol );
118 SetMultiMarkArea( aMarkRange, true, true );
122 SCCOL nStartCol = rRange.aStart.Col();
123 SCROW nStartRow = rRange.aStart.Row();
124 SCCOL nEndCol = rRange.aEnd.Col();
125 SCROW nEndRow = rRange.aEnd.Row();
126 PutInOrder( nStartRow, nEndRow );
127 PutInOrder( nStartCol, nEndCol );
129 aMultiSel.SetMarkArea( nStartCol, nEndCol, nStartRow, nEndRow, bMark );
131 if ( bMultiMarked ) // Update aMultiRange
133 if ( nStartCol < aMultiRange.aStart.Col() )
134 aMultiRange.aStart.SetCol( nStartCol );
135 if ( nStartRow < aMultiRange.aStart.Row() )
136 aMultiRange.aStart.SetRow( nStartRow );
137 if ( nEndCol > aMultiRange.aEnd.Col() )
138 aMultiRange.aEnd.SetCol( nEndCol );
139 if ( nEndRow > aMultiRange.aEnd.Row() )
140 aMultiRange.aEnd.SetRow( nEndRow );
142 else
144 aMultiRange = rRange; // new
145 bMultiMarked = true;
149 void ScMarkData::SetAreaTab( SCTAB nTab )
151 aMarkRange.aStart.SetTab(nTab);
152 aMarkRange.aEnd.SetTab(nTab);
153 aMultiRange.aStart.SetTab(nTab);
154 aMultiRange.aEnd.SetTab(nTab);
157 void ScMarkData::SelectTable( SCTAB nTab, bool bNew )
159 if ( bNew )
161 maTabMarked.insert( nTab );
163 else
165 maTabMarked.erase( nTab );
169 bool ScMarkData::GetTableSelect( SCTAB nTab ) const
171 return (maTabMarked.find( nTab ) != maTabMarked.end());
174 void ScMarkData::SelectOneTable( SCTAB nTab )
176 maTabMarked.clear();
177 maTabMarked.insert( nTab );
180 SCTAB ScMarkData::GetSelectCount() const
182 return static_cast<SCTAB> ( maTabMarked.size() );
185 SCTAB ScMarkData::GetFirstSelected() const
187 if (!maTabMarked.empty())
188 return (*maTabMarked.begin());
190 OSL_FAIL("GetFirstSelected: nothing selected");
191 return 0;
194 SCTAB ScMarkData::GetLastSelected() const
196 if (!maTabMarked.empty())
197 return (*maTabMarked.rbegin());
199 OSL_FAIL("GetLastSelected: nothing selected");
200 return 0;
203 void ScMarkData::SetSelectedTabs(const MarkedTabsType& rTabs)
205 MarkedTabsType aTabs(rTabs.begin(), rTabs.end());
206 maTabMarked.swap(aTabs);
209 void ScMarkData::MarkToMulti()
211 if ( bMarked && !bMarking )
213 SetMultiMarkArea( aMarkRange, !bMarkIsNeg );
214 bMarked = false;
216 // check if all multi mark ranges have been removed
217 if ( bMarkIsNeg && !HasAnyMultiMarks() )
218 ResetMark();
222 void ScMarkData::MarkToSimple()
224 if ( bMarking )
225 return;
227 if ( bMultiMarked && bMarked )
228 MarkToMulti(); // may result in bMarked and bMultiMarked reset
230 if ( !bMultiMarked )
231 return;
233 ScRange aNew = aMultiRange;
235 bool bOk = false;
236 SCCOL nStartCol = aNew.aStart.Col();
237 SCCOL nEndCol = aNew.aEnd.Col();
239 while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nStartCol ) )
240 ++nStartCol;
241 while ( nStartCol < nEndCol && !aMultiSel.HasMarks( nEndCol ) )
242 --nEndCol;
244 // Rows are only taken from MarkArray
245 SCROW nStartRow, nEndRow;
246 if ( aMultiSel.HasOneMark( nStartCol, nStartRow, nEndRow ) )
248 bOk = true;
249 SCROW nCmpStart, nCmpEnd;
250 for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++)
251 if ( !aMultiSel.HasOneMark( nCol, nCmpStart, nCmpEnd )
252 || nCmpStart != nStartRow || nCmpEnd != nEndRow )
253 bOk = false;
256 if (bOk)
258 aNew.aStart.SetCol(nStartCol);
259 aNew.aStart.SetRow(nStartRow);
260 aNew.aEnd.SetCol(nEndCol);
261 aNew.aEnd.SetRow(nEndRow);
263 ResetMark();
264 aMarkRange = aNew;
265 bMarked = true;
266 bMarkIsNeg = false;
270 bool ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, bool bNoSimple ) const
272 if ( bMarked && !bNoSimple && !bMarkIsNeg )
273 if ( aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
274 aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
275 return true;
277 if (bMultiMarked)
279 //TODO: test here for negative Marking ?
281 return aMultiSel.GetMark( nCol, nRow );
284 return false;
287 bool ScMarkData::IsColumnMarked( SCCOL nCol ) const
289 // bMarkIsNeg meanwhile also for columns heads
290 //TODO: GetMarkColumnRanges for completely marked column
292 if ( bMarked && !bMarkIsNeg &&
293 aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol &&
294 aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == mrSheetLimits.mnMaxRow )
295 return true;
297 if ( bMultiMarked && aMultiSel.IsAllMarked( nCol, 0, mrSheetLimits.mnMaxRow ) )
298 return true;
300 return false;
303 bool ScMarkData::IsRowMarked( SCROW nRow ) const
305 // bMarkIsNeg meanwhile also for row heads
306 //TODO: GetMarkRowRanges for completely marked rows
308 if ( bMarked && !bMarkIsNeg &&
309 aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == mrSheetLimits.mnMaxCol &&
310 aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow )
311 return true;
313 if ( bMultiMarked )
314 return aMultiSel.IsRowMarked( nRow );
316 return false;
319 void ScMarkData::MarkFromRangeList( const ScRangeList& rList, bool bReset )
321 if (bReset)
323 maTabMarked.clear();
324 ResetMark();
327 size_t nCount = rList.size();
328 if ( nCount == 1 && !bMarked && !bMultiMarked )
330 const ScRange& rRange = rList[ 0 ];
331 SetMarkArea( rRange );
332 SelectTable( rRange.aStart.Tab(), true );
334 else
336 for (size_t i=0; i < nCount; i++)
338 const ScRange& rRange = rList[ i ];
339 SetMultiMarkArea( rRange );
340 SelectTable( rRange.aStart.Tab(), true );
346 Optimise the case of constructing from a range list, speeds up import.
348 ScMarkData::ScMarkData(const ScSheetLimits& rLimits, const ScRangeList& rList)
349 : aMultiSel(rLimits),
350 mrSheetLimits(rLimits)
352 ResetMark();
354 for (const ScRange& rRange : rList)
355 maTabMarked.insert( rRange.aStart.Tab() );
357 if (rList.size() > 1)
359 bMultiMarked = true;
360 aMultiRange = rList.Combine();
362 aMultiSel.Set( rList );
364 else if (rList.size() == 1)
366 const ScRange& rRange = rList[ 0 ];
367 SetMarkArea( rRange );
372 void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, bool bClear, SCTAB nForTab ) const
374 if (!pList)
375 return;
377 if (bClear)
378 pList->RemoveAll();
380 //TODO: for multiple selected tables enter multiple ranges !!!
382 if ( bMultiMarked )
384 SCTAB nTab = (nForTab < 0 ? aMultiRange.aStart.Tab() : nForTab);
386 SCCOL nStartCol = aMultiRange.aStart.Col();
387 SCCOL nEndCol = aMultiRange.aEnd.Col();
388 for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++)
390 if (aMultiSel.HasMarks( nCol ))
392 // Feeding column-wise fragments to ScRangeList::Join() is a
393 // huge bottleneck, speed this up for multiple columns
394 // consisting of identical row sets by building a column span
395 // first. This is usually the case for filtered data, for
396 // example.
397 SCCOL nToCol = nCol+1;
398 for ( ; nToCol <= nEndCol; ++nToCol)
400 if (!aMultiSel.HasEqualRowsMarked(nCol, nToCol))
401 break;
403 --nToCol;
404 ScRange aRange( nCol, 0, nTab, nToCol, 0, nTab );
405 SCROW nTop, nBottom;
406 ScMultiSelIter aMultiIter( aMultiSel, nCol );
407 while ( aMultiIter.Next( nTop, nBottom ) )
409 aRange.aStart.SetRow( nTop );
410 aRange.aEnd.SetRow( nBottom );
411 pList->Join( aRange );
413 nCol = nToCol;
418 if ( bMarked )
420 if (nForTab < 0)
421 pList->push_back( aMarkRange );
422 else
424 ScRange aRange( aMarkRange );
425 aRange.aStart.SetTab( nForTab );
426 aRange.aEnd.SetTab( nForTab );
427 pList->push_back( aRange );
432 void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const
434 if (!pList)
435 return;
437 ScRangeList aOldList(*pList);
438 pList->RemoveAll(); //TODO: or skip the existing below
440 for (const auto& rTab : maTabMarked)
441 for ( size_t i=0, nCount = aOldList.size(); i<nCount; i++)
443 ScRange aRange = aOldList[ i ];
444 aRange.aStart.SetTab(rTab);
445 aRange.aEnd.SetTab(rTab);
446 pList->push_back( aRange );
450 ScRangeList ScMarkData::GetMarkedRanges() const
452 ScRangeList aRet;
453 FillRangeListWithMarks(&aRet, false);
454 return aRet;
457 ScRangeList ScMarkData::GetMarkedRangesForTab( SCTAB nTab ) const
459 ScRangeList aRet;
460 FillRangeListWithMarks(&aRet, false, nTab);
461 return aRet;
464 std::vector<sc::ColRowSpan> ScMarkData::GetMarkedRowSpans() const
466 typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
468 ScRangeList aRanges = GetMarkedRanges();
469 SpansType aSpans(0, mrSheetLimits.mnMaxRow+1, false);
470 SpansType::const_iterator itPos = aSpans.begin();
472 for (size_t i = 0, n = aRanges.size(); i < n; ++i)
474 const ScRange& r = aRanges[i];
475 itPos = aSpans.insert(itPos, r.aStart.Row(), r.aEnd.Row()+1, true).first;
478 return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
481 std::vector<sc::ColRowSpan> ScMarkData::GetMarkedColSpans() const
484 if (bMultiMarked)
486 SCCOL nStartCol = aMultiRange.aStart.Col();
487 SCCOL nEndCol = aMultiRange.aEnd.Col();
488 if (bMarked)
490 // Use segment tree to merge marked with multi marked.
491 typedef mdds::flat_segment_tree<SCCOLROW, bool> SpansType;
492 SpansType aSpans(0, mrSheetLimits.mnMaxCol+1, false);
493 SpansType::const_iterator itPos = aSpans.begin();
496 if (aMultiSel.GetRowSelArray().HasMarks())
498 itPos = aSpans.insert(itPos, nStartCol, nEndCol+1, true).first;
499 break; // do; all columns marked
502 /* XXX if it turns out that span insert is too slow for lots of
503 * subsequent columns we could gather each span first and then
504 * insert. */
505 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
507 const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
508 if (pMultiArray && pMultiArray->HasMarks())
509 itPos = aSpans.insert(itPos, nCol, nCol+1, true).first;
512 while(false);
514 // Merge marked.
515 aSpans.insert(itPos, aMarkRange.aStart.Col(), aMarkRange.aEnd.Col()+1, true);
517 return sc::toSpanArray<SCCOLROW,sc::ColRowSpan>(aSpans);
519 else
521 // A plain vector is sufficient, avoid segment tree and conversion
522 // to vector overhead.
523 std::vector<sc::ColRowSpan> aVec;
524 if (aMultiSel.GetRowSelArray().HasMarks())
526 aVec.emplace_back( nStartCol, nEndCol);
527 return aVec; // all columns marked
529 sc::ColRowSpan aSpan( -1, -1);
530 for (SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol)
532 const ScMarkArray* pMultiArray = aMultiSel.GetMultiSelArray( nCol );
533 if (pMultiArray && pMultiArray->HasMarks())
535 if (aSpan.mnStart == -1)
536 aSpan.mnStart = nCol;
537 aSpan.mnEnd = nCol;
539 else
541 // Add span gathered so far, if any.
542 if (aSpan.mnStart != -1)
544 aVec.push_back( aSpan);
545 aSpan.mnStart = -1;
549 // Add last span, if any.
550 if (aSpan.mnStart != -1)
551 aVec.push_back( aSpan);
552 return aVec;
556 // Only reached if not multi marked.
557 std::vector<sc::ColRowSpan> aVec;
558 if (bMarked)
560 aVec.emplace_back( aMarkRange.aStart.Col(), aMarkRange.aEnd.Col());
562 return aVec;
565 bool ScMarkData::IsAllMarked( const ScRange& rRange ) const
567 SCCOL nStartCol = rRange.aStart.Col();
568 SCROW nStartRow = rRange.aStart.Row();
569 SCCOL nEndCol = rRange.aEnd.Col();
570 SCROW nEndRow = rRange.aEnd.Row();
572 if ( !bMultiMarked )
574 if ( bMarked && !bMarkIsNeg &&
575 aMarkRange.aStart.Col() <= nStartCol && aMarkRange.aEnd.Col() >= nEndCol &&
576 aMarkRange.aStart.Row() <= nStartRow && aMarkRange.aEnd.Row() >= nEndRow )
577 return true;
578 return false;
581 bool bOk = true;
583 if ( nStartCol == 0 && nEndCol == mrSheetLimits.mnMaxCol )
584 return aMultiSel.IsRowRangeMarked( nStartRow, nEndRow );
586 for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++)
587 if ( !aMultiSel.IsAllMarked( nCol, nStartRow, nEndRow ) )
588 bOk = false;
590 return bOk;
593 SCCOL ScMarkData::GetStartOfEqualColumns( SCCOL nLastCol, SCCOL nMinCol ) const
595 if( !bMultiMarked )
597 if ( bMarked && !bMarkIsNeg )
599 if( aMarkRange.aEnd.Col() >= nMinCol && aMarkRange.aStart.Col() < nLastCol )
600 return aMarkRange.aEnd.Col() + 1;
601 if( aMarkRange.aEnd.Col() >= nLastCol && aMarkRange.aStart.Col() <= nMinCol )
602 return aMarkRange.aStart.Col();
604 return nMinCol;
606 return aMultiSel.GetStartOfEqualColumns( nLastCol, nMinCol );
609 SCROW ScMarkData::GetNextMarked( SCCOL nCol, SCROW nRow, bool bUp ) const
611 if ( !bMultiMarked )
612 return nRow;
614 return aMultiSel.GetNextMarked( nCol, nRow, bUp );
617 bool ScMarkData::HasMultiMarks( SCCOL nCol ) const
619 if ( !bMultiMarked )
620 return false;
622 return aMultiSel.HasMarks( nCol );
625 bool ScMarkData::HasAnyMultiMarks() const
627 if ( !bMultiMarked )
628 return false;
630 return aMultiSel.HasAnyMarks();
633 void ScMarkData::InsertTab( SCTAB nTab )
635 std::set<SCTAB> tabMarked;
636 for (const auto& rTab : maTabMarked)
638 if (rTab < nTab)
639 tabMarked.insert(rTab);
640 else
641 tabMarked.insert(rTab + 1);
643 maTabMarked.swap(tabMarked);
646 void ScMarkData::DeleteTab( SCTAB nTab )
648 std::set<SCTAB> tabMarked;
649 for (const auto& rTab : maTabMarked)
651 if (rTab < nTab)
652 tabMarked.insert(rTab);
653 else if (rTab > nTab)
654 tabMarked.insert(rTab - 1);
656 maTabMarked.swap(tabMarked);
659 void ScMarkData::ShiftCols(const ScDocument& rDoc, SCCOL nStartCol, sal_Int32 nColOffset)
661 if (bMarked)
662 aMarkRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
663 if (bMultiMarked)
665 aMultiRange.IncColIfNotLessThan(rDoc, nStartCol, nColOffset);
666 aMultiSel.ShiftCols(nStartCol, nColOffset);
670 void ScMarkData::ShiftRows(const ScDocument& rDoc, SCROW nStartRow, sal_Int32 nRowOffset)
672 if (bMarked)
673 aMarkRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
674 if (bMultiMarked)
676 aMultiRange.IncRowIfNotLessThan(rDoc, nStartRow, nRowOffset);
677 aMultiSel.ShiftRows(nStartRow, nRowOffset);
681 static void lcl_AddRanges(ScRange& rRangeDest, const ScRange& rNewRange )
683 SCCOL nStartCol = rNewRange.aStart.Col();
684 SCROW nStartRow = rNewRange.aStart.Row();
685 SCCOL nEndCol = rNewRange.aEnd.Col();
686 SCROW nEndRow = rNewRange.aEnd.Row();
687 PutInOrder( nStartRow, nEndRow );
688 PutInOrder( nStartCol, nEndCol );
689 if ( nStartCol < rRangeDest.aStart.Col() )
690 rRangeDest.aStart.SetCol( nStartCol );
691 if ( nStartRow < rRangeDest.aStart.Row() )
692 rRangeDest.aStart.SetRow( nStartRow );
693 if ( nEndCol > rRangeDest.aEnd.Col() )
694 rRangeDest.aEnd.SetCol( nEndCol );
695 if ( nEndRow > rRangeDest.aEnd.Row() )
696 rRangeDest.aEnd.SetRow( nEndRow );
699 void ScMarkData::GetSelectionCover( ScRange& rRange )
701 if( bMultiMarked )
703 rRange = aMultiRange;
704 SCCOL nStartCol = aMultiRange.aStart.Col(), nEndCol = aMultiRange.aEnd.Col();
705 PutInOrder( nStartCol, nEndCol );
706 nStartCol = ( nStartCol == 0 ) ? nStartCol : nStartCol - 1;
707 nEndCol = ( nEndCol == mrSheetLimits.mnMaxCol ) ? nEndCol : nEndCol + 1;
708 std::unique_ptr<ScFlatBoolRowSegments> pPrevColMarkedRows;
709 std::unique_ptr<ScFlatBoolRowSegments> pCurColMarkedRows;
710 std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInTopEnvelope;
711 std::unordered_map<SCROW,ScFlatBoolColSegments> aRowToColSegmentsInBottomEnvelope;
712 ScFlatBoolRowSegments aNoRowsMarked(mrSheetLimits.mnMaxRow);
713 aNoRowsMarked.setFalse( 0, mrSheetLimits.mnMaxRow );
715 bool bPrevColUnMarked = false;
717 for ( SCCOL nCol=nStartCol; nCol <= nEndCol; nCol++ )
719 SCROW nTop, nBottom;
720 bool bCurColUnMarked = !aMultiSel.HasMarks( nCol );
721 if ( !bCurColUnMarked )
723 pCurColMarkedRows.reset( new ScFlatBoolRowSegments(mrSheetLimits.mnMaxRow) );
724 pCurColMarkedRows->setFalse( 0, mrSheetLimits.mnMaxRow );
725 ScMultiSelIter aMultiIter( aMultiSel, nCol );
726 ScFlatBoolRowSegments::ForwardIterator aPrevItr(
727 pPrevColMarkedRows ? *pPrevColMarkedRows
728 : aNoRowsMarked); // For finding left envelope
729 ScFlatBoolRowSegments::ForwardIterator aPrevItr1(
730 pPrevColMarkedRows ? *pPrevColMarkedRows
731 : aNoRowsMarked); // For finding right envelope
732 SCROW nTopPrev = 0, nBottomPrev = 0; // For right envelope
733 while ( aMultiIter.Next( nTop, nBottom ) )
735 pCurColMarkedRows->setTrue( nTop, nBottom );
736 if( bPrevColUnMarked && ( nCol > nStartCol ))
738 ScRange aAddRange(nCol - 1, nTop, aMultiRange.aStart.Tab(),
739 nCol - 1, nBottom, aMultiRange.aStart.Tab());
740 lcl_AddRanges( rRange, aAddRange ); // Left envelope
741 aLeftEnvelope.push_back( aAddRange );
743 else if( nCol > nStartCol )
745 SCROW nTop1 = nTop, nBottom1 = nTop;
746 while( nTop1 <= nBottom && nBottom1 <= nBottom )
748 bool bRangeMarked = false;
749 const bool bHasValue = aPrevItr.getValue( nTop1, bRangeMarked );
750 assert(bHasValue); (void)bHasValue;
751 if( bRangeMarked )
753 nTop1 = aPrevItr.getLastPos() + 1;
754 nBottom1 = nTop1;
756 else
758 nBottom1 = aPrevItr.getLastPos();
759 if( nBottom1 > nBottom )
760 nBottom1 = nBottom;
761 ScRange aAddRange( nCol - 1, nTop1, aMultiRange.aStart.Tab(),
762 nCol - 1, nBottom1, aMultiRange.aStart.Tab() );
763 lcl_AddRanges( rRange, aAddRange ); // Left envelope
764 aLeftEnvelope.push_back( aAddRange );
765 nTop1 = ++nBottom1;
768 while( nTopPrev <= nBottom && nBottomPrev <= nBottom )
770 bool bRangeMarked;
771 const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
772 assert(bHasValue); (void)bHasValue;
773 if( bRangeMarked )
775 nBottomPrev = aPrevItr1.getLastPos();
776 if( nTopPrev < nTop )
778 if( nBottomPrev >= nTop )
780 nBottomPrev = nTop - 1;
781 ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
782 nCol, nBottomPrev, aMultiRange.aStart.Tab());
783 lcl_AddRanges( rRange, aAddRange ); // Right envelope
784 aRightEnvelope.push_back( aAddRange );
785 nTopPrev = nBottomPrev = (nBottom + 1);
787 else
789 ScRange aAddRange( nCol, nTopPrev, aMultiRange.aStart.Tab(),
790 nCol, nBottomPrev, aMultiRange.aStart.Tab());
791 lcl_AddRanges( rRange, aAddRange ); // Right envelope
792 aRightEnvelope.push_back( aAddRange );
793 nTopPrev = ++nBottomPrev;
796 else
797 nTopPrev = nBottomPrev = ( nBottom + 1 );
799 else
801 nBottomPrev = aPrevItr1.getLastPos();
802 nTopPrev = ++nBottomPrev;
806 if( nTop )
808 ScRange aAddRange( nCol, nTop - 1, aMultiRange.aStart.Tab(),
809 nCol, nTop - 1, aMultiRange.aStart.Tab());
810 lcl_AddRanges( rRange, aAddRange ); // Top envelope
811 auto it = aRowToColSegmentsInTopEnvelope.find(nTop - 1);
812 if (it == aRowToColSegmentsInTopEnvelope.end())
813 it = aRowToColSegmentsInTopEnvelope.emplace(nTop - 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
814 it->second.setTrue( nCol, nCol );
816 if( nBottom < mrSheetLimits.mnMaxRow )
818 ScRange aAddRange(nCol, nBottom + 1, aMultiRange.aStart.Tab(),
819 nCol, nBottom + 1, aMultiRange.aStart.Tab());
820 lcl_AddRanges( rRange, aAddRange ); // Bottom envelope
821 auto it = aRowToColSegmentsInBottomEnvelope.find(nBottom + 1);
822 if (it == aRowToColSegmentsInBottomEnvelope.end())
823 it = aRowToColSegmentsInBottomEnvelope.emplace(nBottom + 1, ScFlatBoolColSegments(mrSheetLimits.mnMaxCol)).first;
824 it->second.setTrue( nCol, nCol );
828 while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow && ( nCol > nStartCol ) )
830 bool bRangeMarked;
831 const bool bHasValue = aPrevItr1.getValue( nTopPrev, bRangeMarked );
832 assert(bHasValue); (void)bHasValue;
833 if( bRangeMarked )
835 nBottomPrev = aPrevItr1.getLastPos();
836 ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
837 nCol, nBottomPrev, aMultiRange.aStart.Tab());
838 lcl_AddRanges( rRange, aAddRange ); // Right envelope
839 aRightEnvelope.push_back( aAddRange );
840 nTopPrev = ++nBottomPrev;
842 else
844 nBottomPrev = aPrevItr1.getLastPos();
845 nTopPrev = ++nBottomPrev;
849 else if( nCol > nStartCol )
851 bPrevColUnMarked = true;
852 SCROW nTopPrev = 0, nBottomPrev = 0;
853 bool bRangeMarked = false;
854 ScFlatBoolRowSegments::ForwardIterator aPrevItr(
855 pPrevColMarkedRows ? *pPrevColMarkedRows : aNoRowsMarked);
856 while( nTopPrev <= mrSheetLimits.mnMaxRow && nBottomPrev <= mrSheetLimits.mnMaxRow )
858 const bool bHasValue = aPrevItr.getValue(nTopPrev, bRangeMarked);
859 assert(bHasValue); (void)bHasValue;
860 if( bRangeMarked )
862 nBottomPrev = aPrevItr.getLastPos();
863 ScRange aAddRange(nCol, nTopPrev, aMultiRange.aStart.Tab(),
864 nCol, nBottomPrev, aMultiRange.aStart.Tab());
865 lcl_AddRanges( rRange, aAddRange ); // Right envelope
866 aRightEnvelope.push_back( aAddRange );
867 nTopPrev = ++nBottomPrev;
869 else
871 nBottomPrev = aPrevItr.getLastPos();
872 nTopPrev = ++nBottomPrev;
876 if ( bCurColUnMarked )
877 pPrevColMarkedRows.reset();
878 else
879 pPrevColMarkedRows = std::move( pCurColMarkedRows );
881 for( auto& rKV : aRowToColSegmentsInTopEnvelope )
883 SCCOL nStart = nStartCol;
884 ScFlatBoolColSegments::RangeData aRange;
885 while( nStart <= nEndCol )
887 if( !rKV.second.getRangeData( nStart, aRange ) )
888 break;
889 if( aRange.mbValue ) // is marked
890 aTopEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
891 aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
892 nStart = aRange.mnCol2 + 1;
895 for( auto& rKV : aRowToColSegmentsInBottomEnvelope )
897 SCCOL nStart = nStartCol;
898 ScFlatBoolColSegments::RangeData aRange;
899 while( nStart <= nEndCol )
901 if( !rKV.second.getRangeData( nStart, aRange ) )
902 break;
903 if( aRange.mbValue ) // is marked
904 aBottomEnvelope.push_back( ScRange( aRange.mnCol1, rKV.first, aMultiRange.aStart.Tab(),
905 aRange.mnCol2, rKV.first, aMultiRange.aStart.Tab() ) );
906 nStart = aRange.mnCol2 + 1;
910 else if( bMarked )
912 aMarkRange.PutInOrder();
913 SCROW nRow1, nRow2, nRow1New, nRow2New;
914 SCCOL nCol1, nCol2, nCol1New, nCol2New;
915 SCTAB nTab1, nTab2;
916 aMarkRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
917 nCol1New = nCol1;
918 nCol2New = nCol2;
919 nRow1New = nRow1;
920 nRow2New = nRow2;
921 // Each envelope will have zero or more ranges for single rectangle selection.
922 if( nCol1 > 0 )
924 aLeftEnvelope.push_back( ScRange( nCol1 - 1, nRow1, nTab1, nCol1 - 1, nRow2, nTab2 ) );
925 --nCol1New;
927 if( nRow1 > 0 )
929 aTopEnvelope.push_back( ScRange( nCol1, nRow1 - 1, nTab1, nCol2, nRow1 - 1, nTab2 ) );
930 --nRow1New;
932 if( nCol2 < mrSheetLimits.mnMaxCol )
934 aRightEnvelope.push_back( ScRange( nCol2 + 1, nRow1, nTab1, nCol2 + 1, nRow2, nTab2 ) );
935 ++nCol2New;
937 if( nRow2 < mrSheetLimits.mnMaxRow )
939 aBottomEnvelope.push_back( ScRange( nCol1, nRow2 + 1, nTab1, nCol2, nRow2 + 1, nTab2 ) );
940 ++nRow2New;
942 rRange = ScRange( nCol1New, nRow1New, nTab1, nCol2New, nRow2New, nTab2 );
946 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */