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