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 <olinetab.hxx>
21 #include <address.hxx>
22 #include <table.hxx>
23 
24 #include <osl/diagnose.h>
25 
ScOutlineEntry(SCCOLROW nNewStart,SCCOLROW nNewSize,bool bNewHidden)26 ScOutlineEntry::ScOutlineEntry( SCCOLROW nNewStart, SCCOLROW nNewSize, bool bNewHidden ) :
27     nStart  ( nNewStart ),
28     nSize   ( nNewSize ),
29     bHidden ( bNewHidden ),
30     bVisible( true )
31 {
32 }
33 
ScOutlineEntry(const ScOutlineEntry & rEntry)34 ScOutlineEntry::ScOutlineEntry( const ScOutlineEntry& rEntry ) :
35     nStart  ( rEntry.nStart ),
36     nSize   ( rEntry.nSize ),
37     bHidden ( rEntry.bHidden ),
38     bVisible( rEntry.bVisible )
39 {
40 }
41 
GetEnd() const42 SCCOLROW ScOutlineEntry::GetEnd() const
43 {
44     return nStart+nSize-1;
45 }
46 
Move(SCCOLROW nDelta)47 void ScOutlineEntry::Move( SCCOLROW nDelta )
48 {
49     SCCOLROW nNewPos = nStart + nDelta;
50     if (nNewPos<0)
51     {
52         OSL_FAIL("OutlineEntry < 0");
53         nNewPos = 0;
54     }
55     nStart = nNewPos;
56 }
57 
SetSize(SCSIZE nNewSize)58 void ScOutlineEntry::SetSize( SCSIZE nNewSize )
59 {
60     if (nNewSize>0)
61         nSize = nNewSize;
62     else
63     {
64         OSL_FAIL("ScOutlineEntry Size == 0");
65     }
66 }
67 
SetPosSize(SCCOLROW nNewPos,SCSIZE nNewSize)68 void ScOutlineEntry::SetPosSize( SCCOLROW nNewPos, SCSIZE nNewSize )
69 {
70     nStart = nNewPos;
71     SetSize( nNewSize );
72 }
73 
SetHidden(bool bNewHidden)74 void ScOutlineEntry::SetHidden( bool bNewHidden )
75 {
76     bHidden = bNewHidden;
77 }
78 
SetVisible(bool bNewVisible)79 void ScOutlineEntry::SetVisible( bool bNewVisible )
80 {
81     bVisible = bNewVisible;
82 }
83 
dumpAsString() const84 OString ScOutlineEntry::dumpAsString() const
85 {
86     const char* const pSep = ":";
87     return OString::number(nStart) + pSep + OString::number(nSize) +
88         pSep + (bHidden ? "1" : "0") + pSep + (bVisible ? "1" : "0");
89 }
90 
ScOutlineCollection()91 ScOutlineCollection::ScOutlineCollection() {}
92 
size() const93 size_t ScOutlineCollection::size() const
94 {
95     return m_Entries.size();
96 }
97 
clear()98 void ScOutlineCollection::clear()
99 {
100     m_Entries.clear();
101 }
102 
insert(ScOutlineEntry const & rEntry)103 void ScOutlineCollection::insert(ScOutlineEntry const& rEntry)
104 {
105     SCCOLROW nStart = rEntry.GetStart();
106     m_Entries.insert(std::make_pair(nStart, rEntry));
107 }
108 
begin()109 ScOutlineCollection::iterator ScOutlineCollection::begin()
110 {
111     return m_Entries.begin();
112 }
113 
end()114 ScOutlineCollection::iterator ScOutlineCollection::end()
115 {
116     return m_Entries.end();
117 }
118 
begin() const119 ScOutlineCollection::const_iterator ScOutlineCollection::begin() const
120 {
121     return m_Entries.begin();
122 }
123 
end() const124 ScOutlineCollection::const_iterator ScOutlineCollection::end() const
125 {
126     return m_Entries.end();
127 }
128 
erase(const iterator & pos)129 ScOutlineCollection::iterator ScOutlineCollection::erase(const iterator& pos)
130 {
131     return m_Entries.erase(pos);
132 }
133 
empty() const134 bool ScOutlineCollection::empty() const
135 {
136     return m_Entries.empty();
137 }
138 
FindStart(SCCOLROW nMinStart)139 ScOutlineCollection::iterator ScOutlineCollection::FindStart(SCCOLROW nMinStart)
140 {
141     return m_Entries.lower_bound(nMinStart);
142 }
143 
dumpAsString() const144 OString ScOutlineCollection::dumpAsString() const
145 {
146     OString aOutput;
147     const char* const pGroupEntrySep = ",";
148     for (const auto& rKeyValuePair : m_Entries)
149         aOutput += rKeyValuePair.second.dumpAsString() + pGroupEntrySep;
150 
151     return aOutput;
152 }
153 
ScOutlineArray()154 ScOutlineArray::ScOutlineArray() :
155     nDepth(0) {}
156 
ScOutlineArray(const ScOutlineArray & rArray)157 ScOutlineArray::ScOutlineArray( const ScOutlineArray& rArray ) :
158     nDepth( rArray.nDepth )
159 {
160     for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
161     {
162         const ScOutlineCollection& rColl = rArray.aCollections[nLevel];
163         for (const auto& rEntry : rColl)
164         {
165             const ScOutlineEntry *const pEntry = &rEntry.second;
166             aCollections[nLevel].insert(*pEntry);
167         }
168     }
169 }
170 
FindEntry(SCCOLROW nSearchPos,size_t & rFindLevel,size_t & rFindIndex,size_t nMaxLevel)171 void ScOutlineArray::FindEntry(
172     SCCOLROW nSearchPos, size_t& rFindLevel, size_t& rFindIndex,
173     size_t nMaxLevel )
174 {
175     rFindLevel = rFindIndex = 0;
176 
177     if (nMaxLevel > nDepth)
178         nMaxLevel = nDepth;
179 
180     for (size_t nLevel = 0; nLevel < nMaxLevel; ++nLevel) //TODO: Search backwards?
181     {
182         ScOutlineCollection* pCollect = &aCollections[nLevel];
183         size_t nIndex = 0;
184         for (auto& rEntry : *pCollect)
185         {
186             ScOutlineEntry *const pEntry = &rEntry.second;
187             if (pEntry->GetStart() <= nSearchPos && pEntry->GetEnd() >= nSearchPos)
188             {
189                 rFindLevel = nLevel + 1; // Next Level (for insertion)
190                 rFindIndex = nIndex;
191             }
192             ++nIndex;
193         }
194     }
195 }
196 
Insert(SCCOLROW nStartCol,SCCOLROW nEndCol,bool & rSizeChanged,bool bHidden)197 bool ScOutlineArray::Insert(
198     SCCOLROW nStartCol, SCCOLROW nEndCol, bool& rSizeChanged, bool bHidden )
199 {
200     rSizeChanged = false;
201 
202     size_t nStartLevel, nEndLevel, nStartIndex, nEndIndex;
203     bool bFound = false;
204 
205     bool bCont;
206     sal_uInt16 nFindMax;
207     FindEntry( nStartCol, nStartLevel, nStartIndex ); // nLevel = new Level (old+1)
208     FindEntry( nEndCol, nEndLevel, nEndIndex );
209     nFindMax = std::max(nStartLevel,nEndLevel);
210     do
211     {
212         bCont = false;
213 
214         if (nStartLevel == nEndLevel && nStartIndex == nEndIndex && nStartLevel < SC_OL_MAXDEPTH)
215             bFound = true;
216 
217         if (!bFound && nFindMax>0)
218         {
219             --nFindMax;
220             if (nStartLevel)
221             {
222                 ScOutlineCollection::const_iterator it = aCollections[nStartLevel-1].begin();
223                 std::advance(it, nStartIndex);
224                 if (it->second.GetStart() == nStartCol)
225                     FindEntry(nStartCol, nStartLevel, nStartIndex, nFindMax);
226             }
227 
228             if (nEndLevel)
229             {
230                 ScOutlineCollection::const_iterator it = aCollections[nEndLevel-1].begin();
231                 std::advance(it, nEndIndex);
232                 if (it->second.GetEnd() == nEndCol)
233                     FindEntry(nEndCol, nEndLevel, nEndIndex, nFindMax);
234             }
235             bCont = true;
236         }
237     }
238     while ( !bFound && bCont );
239 
240     if (!bFound)
241         return false;
242 
243     size_t nLevel = nStartLevel;
244 
245     // Move the ones underneath
246     bool bNeedSize = false;
247     if (nDepth > 0)
248     {
249         for (size_t nMoveLevel = nDepth-1; nMoveLevel >= nLevel; --nMoveLevel)
250         {
251             ScOutlineCollection& rColl = aCollections[nMoveLevel];
252             ScOutlineCollection::iterator it = rColl.begin(), itEnd = rColl.end();
253             while (it != itEnd)
254             {
255                 ScOutlineEntry *const pEntry = &it->second;
256                 SCCOLROW nEntryStart = pEntry->GetStart();
257                 if (nEntryStart >= nStartCol && nEntryStart <= nEndCol)
258                 {
259                     if (nMoveLevel >= SC_OL_MAXDEPTH - 1)
260                     {
261                         rSizeChanged = false; // No more room
262                         return false;
263                     }
264                     aCollections[nMoveLevel+1].insert(*pEntry);
265                     it = rColl.erase(it);
266                     if (nMoveLevel == nDepth - 1)
267                         bNeedSize = true;
268                 }
269                 else
270                     ++it;
271             }
272             if (nMoveLevel == 0)
273                 break;
274         }
275     }
276 
277     if (bNeedSize)
278     {
279         ++nDepth;
280         rSizeChanged = true;
281     }
282 
283     if (nDepth <= nLevel)
284     {
285         nDepth = nLevel+1;
286         rSizeChanged = true;
287     }
288 
289     ScOutlineEntry aNewEntry(nStartCol, nEndCol+1-nStartCol, bHidden);
290     aNewEntry.SetVisible( true );
291     aCollections[nLevel].insert(aNewEntry);
292 
293     return true;
294 }
295 
FindTouchedLevel(SCCOLROW nBlockStart,SCCOLROW nBlockEnd,size_t & rFindLevel) const296 bool ScOutlineArray::FindTouchedLevel(
297     SCCOLROW nBlockStart, SCCOLROW nBlockEnd, size_t& rFindLevel) const
298 {
299     bool bFound = false;
300     rFindLevel = 0;
301 
302     for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
303     {
304         const ScOutlineCollection* pCollect = &aCollections[nLevel];
305         for (const auto& rEntry : *pCollect)
306         {
307             const ScOutlineEntry *const pEntry = &rEntry.second;
308             SCCOLROW nStart = pEntry->GetStart();
309             SCCOLROW nEnd   = pEntry->GetEnd();
310 
311             if ( ( nBlockStart>=nStart && nBlockStart<=nEnd ) ||
312                  ( nBlockEnd  >=nStart && nBlockEnd  <=nEnd ) )
313             {
314                 rFindLevel = nLevel; // Actual Level
315                 bFound = true;
316             }
317         }
318     }
319 
320     return bFound;
321 }
322 
PromoteSub(SCCOLROW nStartPos,SCCOLROW nEndPos,size_t nStartLevel)323 void ScOutlineArray::PromoteSub(SCCOLROW nStartPos, SCCOLROW nEndPos, size_t nStartLevel)
324 {
325     if (nStartLevel==0)
326     {
327         OSL_FAIL("PromoteSub with Level 0");
328         return;
329     }
330 
331     for (size_t nLevel = nStartLevel; nLevel < nDepth; ++nLevel)
332     {
333         ScOutlineCollection& rColl = aCollections[nLevel];
334         ScOutlineCollection::iterator it = rColl.begin(), itEnd = rColl.end();
335         while (it != itEnd)
336         {
337             ScOutlineEntry *const pEntry = &it->second;
338             SCCOLROW nStart = pEntry->GetStart();
339             SCCOLROW nEnd   = pEntry->GetEnd();
340             if (nStart >= nStartPos && nEnd <= nEndPos)
341             {
342                 aCollections[nLevel-1].insert(*pEntry);
343 
344                 it = rColl.erase(it);
345             }
346             else
347                 ++it;
348         }
349 
350         it = rColl.begin();
351         itEnd = rColl.end();
352 
353         while (it != itEnd)
354         {
355             ScOutlineEntry *const pEntry = &it->second;
356             SCCOLROW nStart = pEntry->GetStart();
357             SCCOLROW nEnd   = pEntry->GetEnd();
358             if (nStart >= nStartPos && nEnd <= nEndPos)
359             {
360                 aCollections[nLevel-1].insert(*pEntry);
361 
362                 it = rColl.erase(it);
363             }
364             else
365                 ++it;
366         }
367     }
368 }
369 
370 /**
371  * Adapt nDepth for empty Levels
372  */
DecDepth()373 bool ScOutlineArray::DecDepth()
374 {
375     bool bChanged = false;
376     bool bCont;
377     do
378     {
379         bCont = false;
380         if (nDepth)
381         {
382             if (aCollections[nDepth-1].empty())
383             {
384                 --nDepth;
385                 bChanged = true;
386                 bCont = true;
387             }
388         }
389     }
390     while (bCont);
391 
392     return bChanged;
393 }
394 
Remove(SCCOLROW nBlockStart,SCCOLROW nBlockEnd,bool & rSizeChanged)395 bool ScOutlineArray::Remove( SCCOLROW nBlockStart, SCCOLROW nBlockEnd, bool& rSizeChanged )
396 {
397     size_t nLevel;
398     FindTouchedLevel( nBlockStart, nBlockEnd, nLevel );
399 
400     ScOutlineCollection* pCollect = &aCollections[nLevel];
401     ScOutlineCollection::iterator it = pCollect->begin(), itEnd = pCollect->end();
402     bool bAny = false;
403     while (it != itEnd)
404     {
405         ScOutlineEntry *const pEntry = &it->second;
406         SCCOLROW nStart = pEntry->GetStart();
407         SCCOLROW nEnd   = pEntry->GetEnd();
408         if (nBlockStart <= nEnd && nBlockEnd >= nStart)
409         {
410             // Overlaps
411             pCollect->erase(it);
412             PromoteSub( nStart, nEnd, nLevel+1 );
413             itEnd = pCollect->end();
414             it = pCollect->FindStart( nEnd+1 );
415             bAny = true;
416         }
417         else
418             ++it;
419     }
420 
421     if (bAny) // Adapt Depth
422         if (DecDepth())
423             rSizeChanged = true;
424 
425     return bAny;
426 }
427 
GetEntry(size_t nLevel,size_t nIndex)428 ScOutlineEntry* ScOutlineArray::GetEntry(size_t nLevel, size_t nIndex)
429 {
430     if (nLevel >= nDepth)
431         return nullptr;
432 
433     ScOutlineCollection& rColl = aCollections[nLevel];
434     if (nIndex >= rColl.size())
435         return nullptr;
436 
437     ScOutlineCollection::iterator it = rColl.begin();
438     std::advance(it, nIndex);
439     return &it->second;
440 }
441 
GetEntry(size_t nLevel,size_t nIndex) const442 const ScOutlineEntry* ScOutlineArray::GetEntry(size_t nLevel, size_t nIndex) const
443 {
444     if (nLevel >= nDepth)
445         return nullptr;
446 
447     const ScOutlineCollection& rColl = aCollections[nLevel];
448     if (nIndex >= rColl.size())
449         return nullptr;
450 
451     ScOutlineCollection::const_iterator it = rColl.begin();
452     std::advance(it, nIndex);
453     return &it->second;
454 }
455 
GetCount(size_t nLevel) const456 size_t ScOutlineArray::GetCount(size_t nLevel) const
457 {
458     if (nLevel >= nDepth)
459         return 0;
460 
461     return aCollections[nLevel].size();
462 }
463 
GetEntryByPos(size_t nLevel,SCCOLROW nPos) const464 const ScOutlineEntry* ScOutlineArray::GetEntryByPos(size_t nLevel, SCCOLROW nPos) const
465 {
466     if (nLevel >= nDepth)
467         return nullptr;
468 
469     const ScOutlineCollection& rColl = aCollections[nLevel];
470     ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
471         [&nPos](const auto& rEntry) {
472             const ScOutlineEntry *const pEntry = &rEntry.second;
473             return pEntry->GetStart() <= nPos && nPos <= pEntry->GetEnd();
474         });
475     if (it != rColl.end())
476         return &it->second;
477 
478     return nullptr;
479 }
480 
GetEntryIndex(size_t nLevel,SCCOLROW nPos,size_t & rnIndex) const481 bool ScOutlineArray::GetEntryIndex(size_t nLevel, SCCOLROW nPos, size_t& rnIndex) const
482 {
483     if (nLevel >= nDepth)
484         return false;
485 
486     // Found entry contains passed position
487     const ScOutlineCollection& rColl = aCollections[nLevel];
488     ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
489         [&nPos](const auto& rEntry) {
490             const ScOutlineEntry *const p = &rEntry.second;
491             return p->GetStart() <= nPos && nPos <= p->GetEnd();
492         });
493     if (it != rColl.end())
494     {
495         rnIndex = std::distance(rColl.begin(), it);
496         return true;
497     }
498     return false;
499 }
500 
GetEntryIndexInRange(size_t nLevel,SCCOLROW nBlockStart,SCCOLROW nBlockEnd,size_t & rnIndex) const501 bool ScOutlineArray::GetEntryIndexInRange(
502     size_t nLevel, SCCOLROW nBlockStart, SCCOLROW nBlockEnd, size_t& rnIndex) const
503 {
504     if (nLevel >= nDepth)
505         return false;
506 
507     // Found entry will be completely inside of passed range
508     const ScOutlineCollection& rColl = aCollections[nLevel];
509     ScOutlineCollection::const_iterator it = std::find_if(rColl.begin(), rColl.end(),
510         [&nBlockStart, &nBlockEnd](const auto& rEntry) {
511             const ScOutlineEntry *const p = &rEntry.second;
512             return nBlockStart <= p->GetStart() && p->GetEnd() <= nBlockEnd;
513         });
514     if (it != rColl.end())
515     {
516         rnIndex = std::distance(rColl.begin(), it);
517         return true;
518     }
519     return false;
520 }
521 
SetVisibleBelow(size_t nLevel,size_t nEntry,bool bValue,bool bSkipHidden)522 void ScOutlineArray::SetVisibleBelow(
523     size_t nLevel, size_t nEntry, bool bValue, bool bSkipHidden)
524 {
525     const ScOutlineEntry* pEntry = GetEntry( nLevel, nEntry );
526     if (!pEntry)
527         return;
528 
529     SCCOLROW nStart = pEntry->GetStart();
530     SCCOLROW nEnd   = pEntry->GetEnd();
531 
532     for (size_t nSubLevel = nLevel+1; nSubLevel < nDepth; ++nSubLevel)
533     {
534         ScOutlineCollection& rColl = aCollections[nSubLevel];
535         size_t nPos = 0;
536         for (auto& rEntry : rColl)
537         {
538             ScOutlineEntry *const p = &rEntry.second;
539             if (p->GetStart() >= nStart && p->GetEnd() <= nEnd)
540             {
541                 p->SetVisible(bValue);
542                 if (bSkipHidden && !p->IsHidden())
543                 {
544                     SetVisibleBelow(nSubLevel, nPos, bValue, true);
545                 }
546             }
547             ++nPos;
548         }
549 
550         if (bSkipHidden)
551             nSubLevel = nDepth; // Bail out
552     }
553 }
554 
GetRange(SCCOLROW & rStart,SCCOLROW & rEnd) const555 void ScOutlineArray::GetRange(SCCOLROW& rStart, SCCOLROW& rEnd) const
556 {
557     const ScOutlineCollection& rColl = aCollections[0];
558     if (!rColl.empty())
559     {
560         ScOutlineCollection::const_iterator it = rColl.begin();
561         rStart = it->second.GetStart();
562         std::advance(it, rColl.size()-1);
563         rEnd = it->second.GetEnd();
564     }
565     else
566         rStart = rEnd = 0;
567 }
568 
ExtendBlock(size_t nLevel,SCCOLROW & rBlkStart,SCCOLROW & rBlkEnd)569 void ScOutlineArray::ExtendBlock(size_t nLevel, SCCOLROW& rBlkStart, SCCOLROW& rBlkEnd)
570 {
571     if (nLevel >= nDepth)
572         return;
573 
574     const ScOutlineCollection& rColl = aCollections[nLevel];
575     for (const auto& rEntry : rColl)
576     {
577         const ScOutlineEntry *const pEntry = &rEntry.second;
578         SCCOLROW nStart = pEntry->GetStart();
579         SCCOLROW nEnd   = pEntry->GetEnd();
580 
581         if (rBlkStart <= nEnd && rBlkEnd >= nStart)
582         {
583             if (nStart < rBlkStart)
584                 rBlkStart = nStart;
585             if (nEnd > rBlkEnd)
586                 rBlkEnd = nEnd;
587         }
588     }
589 }
590 
TestInsertSpace(SCSIZE nSize,SCCOLROW nMaxVal) const591 bool ScOutlineArray::TestInsertSpace(SCSIZE nSize, SCCOLROW nMaxVal) const
592 {
593     const ScOutlineCollection& rColl = aCollections[0];
594     if (rColl.empty())
595         return true;
596 
597     ScOutlineCollection::const_iterator it = rColl.begin();
598     std::advance(it, rColl.size()-1);
599     SCCOLROW nEnd = it->second.GetEnd();
600     return sal::static_int_cast<SCCOLROW>(nEnd+nSize) <= nMaxVal;
601 }
602 
InsertSpace(SCCOLROW nStartPos,SCSIZE nSize)603 void ScOutlineArray::InsertSpace(SCCOLROW nStartPos, SCSIZE nSize)
604 {
605     ScSubOutlineIterator aIter( this );
606     ScOutlineEntry* pEntry;
607     while ((pEntry = aIter.GetNext()) != nullptr)
608     {
609         if ( pEntry->GetStart() >= nStartPos )
610             pEntry->Move(static_cast<SCCOLROW>(nSize));
611         else
612         {
613             SCCOLROW nEnd = pEntry->GetEnd();
614             // Always expand if inserted within the group
615             // When inserting at the end, only if the group is not hidden
616             if ( nEnd >= nStartPos || ( nEnd+1 >= nStartPos && !pEntry->IsHidden() ) )
617             {
618                 SCSIZE nEntrySize = pEntry->GetSize();
619                 nEntrySize += nSize;
620                 pEntry->SetSize( nEntrySize );
621             }
622         }
623     }
624 }
625 
DeleteSpace(SCCOLROW nStartPos,SCSIZE nSize)626 bool ScOutlineArray::DeleteSpace(SCCOLROW nStartPos, SCSIZE nSize)
627 {
628     SCCOLROW nEndPos = nStartPos + nSize - 1;
629     bool bNeedSave = false; // Do we need the original one for Undo?
630     bool bChanged = false; // For Level test
631 
632     ScSubOutlineIterator aIter( this );
633     ScOutlineEntry* pEntry;
634     while((pEntry=aIter.GetNext())!=nullptr)
635     {
636         SCCOLROW nEntryStart = pEntry->GetStart();
637         SCCOLROW nEntryEnd   = pEntry->GetEnd();
638         SCSIZE nEntrySize    = pEntry->GetSize();
639 
640         if ( nEntryEnd >= nStartPos )
641         {
642             if ( nEntryStart > nEndPos ) // Right
643                 pEntry->Move(-static_cast<SCCOLROW>(nSize));
644             else if ( nEntryStart < nStartPos && nEntryEnd >= nEndPos ) // Outside
645                 pEntry->SetSize( nEntrySize-nSize );
646             else
647             {
648                 bNeedSave = true;
649                 if ( nEntryStart >= nStartPos && nEntryEnd <= nEndPos ) // Inside
650                 {
651                     aIter.DeleteLast();
652                     bChanged = true;
653                 }
654                 else if ( nEntryStart >= nStartPos ) // Top right
655                     pEntry->SetPosSize( nStartPos, static_cast<SCSIZE>(nEntryEnd-nEndPos) );
656                 else // Top left
657                     pEntry->SetSize( static_cast<SCSIZE>(nStartPos-nEntryStart) );
658             }
659         }
660     }
661 
662     if (bChanged)
663         DecDepth();
664 
665     return bNeedSave;
666 }
667 
ManualAction(SCCOLROW nStartPos,SCCOLROW nEndPos,bool bShow,const ScTable & rTable,bool bCol)668 bool ScOutlineArray::ManualAction(
669     SCCOLROW nStartPos, SCCOLROW nEndPos, bool bShow, const ScTable& rTable, bool bCol)
670 {
671     bool bModified = false;
672     ScSubOutlineIterator aIter( this );
673     ScOutlineEntry* pEntry;
674     while((pEntry=aIter.GetNext())!=nullptr)
675     {
676         SCCOLROW nEntryStart = pEntry->GetStart();
677         SCCOLROW nEntryEnd   = pEntry->GetEnd();
678 
679         if (nEntryEnd>=nStartPos && nEntryStart<=nEndPos)
680         {
681             if ( pEntry->IsHidden() == bShow )
682             {
683                 // #i12341# hide if all columns/rows are hidden, show if at least one
684                 // is visible
685                 SCCOLROW nEnd = rTable.LastHiddenColRow(nEntryStart, bCol);
686                 bool bAllHidden = (nEntryEnd <= nEnd && nEnd <
687                         ::std::numeric_limits<SCCOLROW>::max());
688 
689                 bool bToggle = ( bShow != bAllHidden );
690                 if ( bToggle )
691                 {
692                     pEntry->SetHidden( !bShow );
693                     SetVisibleBelow( aIter.LastLevel(), aIter.LastEntry(), bShow, bShow );
694                     bModified = true;
695                 }
696             }
697         }
698     }
699     return bModified;
700 }
701 
RemoveAll()702 void ScOutlineArray::RemoveAll()
703 {
704     for (size_t nLevel = 0; nLevel < nDepth; ++nLevel)
705         aCollections[nLevel].clear();
706 
707     nDepth = 0;
708 }
709 
finalizeImport(const ScTable & rTable)710 void ScOutlineArray::finalizeImport(const ScTable& rTable)
711 {
712     ScSubOutlineIterator aIter( this );
713     ScOutlineEntry* pEntry;
714     while((pEntry=aIter.GetNext())!=nullptr)
715     {
716 
717         if (!pEntry->IsHidden())
718             continue;
719 
720         SCCOLROW nEntryStart = pEntry->GetStart();
721         SCCOLROW nEntryEnd   = pEntry->GetEnd();
722         SCCOLROW nEnd = rTable.LastHiddenColRow(nEntryStart, false/*bCol*/);
723         bool bAllHidden = (nEntryEnd <= nEnd && nEnd <
724                 ::std::numeric_limits<SCCOLROW>::max());
725 
726         pEntry->SetHidden(bAllHidden);
727         SetVisibleBelow(aIter.LastLevel(), aIter.LastEntry(), !bAllHidden, !bAllHidden);
728     }
729 }
730 
dumpAsString() const731 OString ScOutlineArray::dumpAsString() const
732 {
733     OString aOutput;
734     const char* const pLevelSep = " ";
735     for (const auto& rCollection : aCollections)
736     {
737         if (rCollection.empty())
738             continue;
739         aOutput += rCollection.dumpAsString() + pLevelSep;
740     }
741 
742     return aOutput;
743 }
744 
ScOutlineTable()745 ScOutlineTable::ScOutlineTable()
746 {
747 }
748 
ScOutlineTable(const ScOutlineTable & rOutline)749 ScOutlineTable::ScOutlineTable( const ScOutlineTable& rOutline ) :
750     aColOutline( rOutline.aColOutline ),
751     aRowOutline( rOutline.aRowOutline )
752 {
753 }
754 
TestInsertCol(SCSIZE nSize)755 bool ScOutlineTable::TestInsertCol( SCSIZE nSize )
756 {
757     return aColOutline.TestInsertSpace( nSize, MAXCOL );
758 }
759 
InsertCol(SCCOL nStartCol,SCSIZE nSize)760 void ScOutlineTable::InsertCol( SCCOL nStartCol, SCSIZE nSize )
761 {
762     aColOutline.InsertSpace( nStartCol, nSize );
763 }
764 
DeleteCol(SCCOL nStartCol,SCSIZE nSize)765 bool ScOutlineTable::DeleteCol( SCCOL nStartCol, SCSIZE nSize )
766 {
767     return aColOutline.DeleteSpace( nStartCol, nSize );
768 }
769 
TestInsertRow(SCSIZE nSize)770 bool ScOutlineTable::TestInsertRow( SCSIZE nSize )
771 {
772     return aRowOutline.TestInsertSpace( nSize, MAXROW );
773 }
774 
InsertRow(SCROW nStartRow,SCSIZE nSize)775 void ScOutlineTable::InsertRow( SCROW nStartRow, SCSIZE nSize )
776 {
777     aRowOutline.InsertSpace( nStartRow, nSize );
778 }
779 
DeleteRow(SCROW nStartRow,SCSIZE nSize)780 bool ScOutlineTable::DeleteRow( SCROW nStartRow, SCSIZE nSize )
781 {
782     return aRowOutline.DeleteSpace( nStartRow, nSize );
783 }
784 
ScSubOutlineIterator(ScOutlineArray * pOutlineArray)785 ScSubOutlineIterator::ScSubOutlineIterator( ScOutlineArray* pOutlineArray ) :
786         pArray( pOutlineArray ),
787         nStart( 0 ),
788         nEnd( SCCOLROW_MAX ), // Iterate over all of them
789         nSubLevel( 0 ),
790         nSubEntry( 0 )
791 {
792     nDepth = pArray->nDepth;
793 }
794 
ScSubOutlineIterator(ScOutlineArray * pOutlineArray,size_t nLevel,size_t nEntry)795 ScSubOutlineIterator::ScSubOutlineIterator(
796     ScOutlineArray* pOutlineArray, size_t nLevel, size_t nEntry ) :
797         pArray( pOutlineArray )
798 {
799     const ScOutlineCollection& rColl = pArray->aCollections[nLevel];
800     ScOutlineCollection::const_iterator it = rColl.begin();
801     std::advance(it, nEntry);
802     const ScOutlineEntry* pEntry = &it->second;
803     nStart = pEntry->GetStart();
804     nEnd   = pEntry->GetEnd();
805     nSubLevel = nLevel + 1;
806     nSubEntry = 0;
807     nDepth = pArray->nDepth;
808 }
809 
GetNext()810 ScOutlineEntry* ScSubOutlineIterator::GetNext()
811 {
812     ScOutlineEntry* pEntry = nullptr;
813     bool bFound = false;
814     do
815     {
816         if (nSubLevel >= nDepth)
817             return nullptr;
818 
819         ScOutlineCollection& rColl = pArray->aCollections[nSubLevel];
820         if (nSubEntry < rColl.size())
821         {
822             ScOutlineCollection::iterator it = rColl.begin();
823             std::advance(it, nSubEntry);
824             pEntry = &it->second;
825 
826             if (pEntry->GetStart() >= nStart && pEntry->GetEnd() <= nEnd)
827                 bFound = true;
828 
829             ++nSubEntry;
830         }
831         else
832         {
833             // Go to the next sub-level
834             nSubEntry = 0;
835             ++nSubLevel;
836         }
837     }
838     while (!bFound);
839     return pEntry; // nSubLevel valid, if pEntry != 0
840 }
841 
LastEntry() const842 size_t ScSubOutlineIterator::LastEntry() const
843 {
844     if (nSubEntry == 0)
845     {
846         OSL_FAIL("ScSubOutlineIterator::LastEntry before GetNext");
847         return 0;
848     }
849     return nSubEntry-1;
850 }
851 
DeleteLast()852 void ScSubOutlineIterator::DeleteLast()
853 {
854     if (nSubLevel >= nDepth)
855     {
856         OSL_FAIL("ScSubOutlineIterator::DeleteLast after End");
857         return;
858     }
859     if (nSubEntry == 0)
860     {
861         OSL_FAIL("ScSubOutlineIterator::DeleteLast before GetNext");
862         return;
863     }
864 
865     --nSubEntry;
866     ScOutlineCollection& rColl = pArray->aCollections[nSubLevel];
867     assert(nSubEntry < rColl.size());
868     ScOutlineCollection::iterator it = rColl.begin();
869     std::advance(it, nSubEntry);
870     rColl.erase(it);
871 }
872 
873 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
874