xref: /core/sc/source/core/data/document.cxx (revision ec7ba61a)
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 <scitems.hxx>
21 
22 #include <editeng/boxitem.hxx>
23 #include <editeng/editobj.hxx>
24 #include <svx/sdrundomanager.hxx>
25 #include <svx/svditer.hxx>
26 #include <sfx2/objsh.hxx>
27 #include <sfx2/viewsh.hxx>
28 #include <sfx2/docfile.hxx>
29 #include <svl/poolcach.hxx>
30 #include <svl/zforlist.hxx>
31 #include <unotools/charclass.hxx>
32 #include <unotools/transliterationwrapper.hxx>
33 #include <tools/urlobj.hxx>
34 #include <sal/log.hxx>
35 
36 #include <com/sun/star/text/WritingMode2.hpp>
37 #include <com/sun/star/script/vba/XVBACompatibility.hpp>
38 #include <com/sun/star/sheet/TablePageBreakData.hpp>
39 #include <com/sun/star/lang/NotInitializedException.hpp>
40 
41 #include <document.hxx>
42 #include <table.hxx>
43 #include <column.hxx>
44 #include <attrib.hxx>
45 #include <attarray.hxx>
46 #include <patattr.hxx>
47 #include <rangenam.hxx>
48 #include <poolhelp.hxx>
49 #include <docpool.hxx>
50 #include <stlpool.hxx>
51 #include <stlsheet.hxx>
52 #include <globstr.hrc>
53 #include <scresid.hxx>
54 #include <dbdata.hxx>
55 #include <chartlis.hxx>
56 #include <rangelst.hxx>
57 #include <markdata.hxx>
58 #include <drwlayer.hxx>
59 #include <validat.hxx>
60 #include <prnsave.hxx>
61 #include <chgtrack.hxx>
62 #include <hints.hxx>
63 #include <detdata.hxx>
64 #include <dpobject.hxx>
65 #include <detfunc.hxx>
66 #include <scmod.hxx>
67 #include <dociter.hxx>
68 #include <progress.hxx>
69 #include <autonamecache.hxx>
70 #include <bcaslot.hxx>
71 #include <postit.hxx>
72 #include <clipparam.hxx>
73 #include <defaultsoptions.hxx>
74 #include <editutil.hxx>
75 #include <stringutil.hxx>
76 #include <formulaiter.hxx>
77 #include <formulacell.hxx>
78 #include <clipcontext.hxx>
79 #include <listenercontext.hxx>
80 #include <scopetools.hxx>
81 #include <refupdatecontext.hxx>
82 #include <formulagroup.hxx>
83 #include <tokenstringcontext.hxx>
84 #include <compressedarray.hxx>
85 #include <docsh.hxx>
86 #include <brdcst.hxx>
87 
88 #include <formula/vectortoken.hxx>
89 
90 #include <limits>
91 #include <memory>
92 #include <utility>
93 
94 #include <comphelper/lok.hxx>
95 #include <LibreOfficeKit/LibreOfficeKitEnums.h>
96 
97 #include <mtvelements.hxx>
98 
99 using ::editeng::SvxBorderLine;
100 using namespace ::com::sun::star;
101 
102 namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
103 using ::com::sun::star::uno::Sequence;
104 using ::com::sun::star::sheet::TablePageBreakData;
105 using ::std::set;
106 
107 namespace {
108 
109 std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
110 {
111     SCTAB nTabStart = MAXTAB;
112     SCTAB nTabEnd = 0;
113     SCTAB nMax = static_cast<SCTAB>(rTables.size());
114     for (const auto& rTab : rMark)
115     {
116         if (rTab >= nMax)
117             break;
118 
119         if (!rTables[rTab])
120             continue;
121 
122         if (rTab < nTabStart)
123             nTabStart = rTab;
124         nTabEnd = rTab;
125     }
126 
127     return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
128 }
129 
130 }
131 
132 struct ScDefaultAttr
133 {
134     const ScPatternAttr*    pAttr;
135     SCROW                   nFirst;
136     SCSIZE                  nCount;
137     explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {}
138 };
139 
140 struct ScLessDefaultAttr
141 {
142     bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const
143     {
144         return rValue1.pAttr < rValue2.pAttr;
145     }
146 };
147 
148 typedef std::set<ScDefaultAttr, ScLessDefaultAttr>  ScDefaultAttrSet;
149 
150 void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
151 {
152     if ( ValidTab(nTab) && ( nTab >= static_cast<SCTAB>(maTabs.size()) ||!maTabs[nTab]) )
153     {
154         // Get Custom prefix
155         const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
156         OUString aString = rOpt.GetInitTabPrefix();
157 
158         aString += OUString::number(nTab+1);
159         if ( _bNeedsNameCheck )
160             CreateValidTabName( aString );  // no doubles
161         if (nTab < static_cast<SCTAB>(maTabs.size()))
162         {
163             maTabs[nTab].reset( new ScTable(this, nTab, aString) );
164         }
165         else
166         {
167             while(nTab > static_cast<SCTAB>(maTabs.size()))
168                 maTabs.push_back(nullptr);
169             maTabs.emplace_back( new ScTable(this, nTab, aString) );
170         }
171         maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
172     }
173 }
174 
175 bool ScDocument::HasTable( SCTAB nTab ) const
176 {
177     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
178         if (maTabs[nTab])
179             return true;
180 
181     return false;
182 }
183 
184 bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
185 {
186     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
187     {
188         if (maTabs[nTab])
189         {
190             rHashCode = maTabs[nTab]->GetHashCode();
191             return true;
192         }
193     }
194     return false;
195 }
196 
197 bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
198 {
199     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
200     {
201         if (maTabs[nTab])
202         {
203             rName = maTabs[nTab]->GetName();
204             return true;
205         }
206     }
207     rName.clear();
208     return false;
209 }
210 
211 OUString ScDocument::GetCopyTabName( SCTAB nTab ) const
212 {
213     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
214         return maTabNames[nTab];
215     return OUString();
216 }
217 
218 bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
219 {
220     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
221     {
222         if (maTabs[nTab])
223         {
224             maTabs[nTab]->SetCodeName( rName );
225             return true;
226         }
227     }
228     SAL_WARN("sc",  "can't set code name " << rName );
229     return false;
230 }
231 
232 bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
233 {
234     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
235         if (maTabs[nTab])
236         {
237             rName = maTabs[nTab]->GetCodeName();
238             return true;
239         }
240     rName.clear();
241     return false;
242 }
243 
244 bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
245 {
246     OUString aUpperName;
247     static OUString aCacheName, aCacheUpperName;
248 
249     assert(!IsThreadedGroupCalcInProgress());
250     if (aCacheName != rName)
251     {
252         aCacheName = rName;
253         // surprisingly slow ...
254         aCacheUpperName = ScGlobal::pCharClass->uppercase(rName);
255     }
256     aUpperName = aCacheUpperName;
257 
258     for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++)
259         if (maTabs[i])
260         {
261             if (aUpperName == maTabs[i]->GetUpperName())
262             {
263                 rTab = i;
264                 return true;
265             }
266         }
267     rTab = 0;
268     return false;
269 }
270 
271 std::vector<OUString> ScDocument::GetAllTableNames() const
272 {
273     std::vector<OUString> aNames;
274     aNames.reserve(maTabs.size());
275     for (const auto& a : maTabs)
276     {
277         // Positions need to be preserved for ScCompiler and address convention
278         // context, so still push an empty string for NULL tabs.
279         OUString aName;
280         if (a)
281         {
282             const ScTable& rTab = *a;
283             aName = rTab.GetName();
284         }
285         aNames.push_back(aName);
286     }
287 
288     return aNames;
289 }
290 
291 ScDBData* ScDocument::GetAnonymousDBData(SCTAB nTab)
292 {
293     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
294         return maTabs[nTab]->GetAnonymousDBData();
295     return nullptr;
296 }
297 
298 SCTAB ScDocument::GetTableCount() const
299 {
300     return static_cast<SCTAB>(maTabs.size());
301 }
302 
303 void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
304 {
305     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
306         maTabs[nTab]->SetAnonymousDBData(std::move(pDBData));
307 }
308 
309 void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
310 {
311     mpAnonymousDBData = std::move(pDBData);
312 }
313 
314 ScDBData* ScDocument::GetAnonymousDBData()
315 {
316     return mpAnonymousDBData.get();
317 }
318 
319 bool ScDocument::ValidTabName( const OUString& rName )
320 {
321     if (rName.isEmpty())
322         return false;
323     sal_Int32 nLen = rName.getLength();
324 
325 #if 1
326     // Restrict sheet names to what Excel accepts.
327     /* TODO: We may want to remove this restriction for full ODFF compliance.
328      * Merely loading and calculating ODF documents using these characters in
329      * sheet names is not affected by this, but all sheet name editing and
330      * copying functionality is, maybe falling back to "Sheet4" or similar. */
331     for (sal_Int32 i = 0; i < nLen; ++i)
332     {
333         const sal_Unicode c = rName[i];
334         switch (c)
335         {
336             case ':':
337             case '\\':
338             case '/':
339             case '?':
340             case '*':
341             case '[':
342             case ']':
343                 // these characters are not allowed to match XL's convention.
344                 return false;
345             case '\'':
346                 if (i == 0 || i == nLen - 1)
347                     // single quote is not allowed at the first or last
348                     // character position.
349                     return false;
350             break;
351         }
352     }
353 #endif
354 
355     return true;
356 }
357 
358 bool ScDocument::ValidNewTabName( const OUString& rName ) const
359 {
360     bool bValid = ValidTabName(rName);
361     if (!bValid)
362         return false;
363     OUString aUpperName = ScGlobal::pCharClass->uppercase(rName);
364     for (const auto& a : maTabs)
365     {
366         if (!a)
367             continue;
368         const OUString& rOldName = a->GetUpperName();
369         bValid = rOldName != aUpperName;
370         if (!bValid)
371             break;
372     }
373     return bValid;
374 }
375 
376 void ScDocument::CreateValidTabName(OUString& rName) const
377 {
378     if ( !ValidTabName(rName) )
379     {
380         // Find new one
381 
382         // Get Custom prefix
383         const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
384         const OUString& aStrTable = rOpt.GetInitTabPrefix();
385 
386         bool         bOk   = false;
387 
388         // First test if the prefix is valid, if so only avoid doubles
389         bool bPrefix = ValidTabName( aStrTable );
390         OSL_ENSURE(bPrefix, "Invalid Table Name");
391         SCTAB nDummy;
392 
393         for ( SCTAB i = static_cast<SCTAB>(maTabs.size())+1; !bOk ; i++ )
394         {
395             OUStringBuffer aBuf;
396             aBuf.append(aStrTable);
397             aBuf.append(static_cast<sal_Int32>(i));
398             rName = aBuf.makeStringAndClear();
399             if (bPrefix)
400                 bOk = ValidNewTabName( rName );
401             else
402                 bOk = !GetTable( rName, nDummy );
403         }
404     }
405     else
406     {
407         // testing the supplied Name
408 
409         if ( !ValidNewTabName(rName) )
410         {
411             SCTAB i = 1;
412             OUStringBuffer aName;
413             do
414             {
415                 i++;
416                 aName = rName;
417                 aName.append('_');
418                 aName.append(static_cast<sal_Int32>(i));
419             }
420             while (!ValidNewTabName(aName.toString()) && (i < MAXTAB+1));
421             rName = aName.makeStringAndClear();
422         }
423     }
424 }
425 
426 void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
427 {
428     aNames.clear();//ensure that the vector is empty
429 
430     // Get Custom prefix
431     const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
432     const OUString& aStrTable = rOpt.GetInitTabPrefix();
433 
434     OUStringBuffer rName;
435 
436     // First test if the prefix is valid, if so only avoid doubles
437     bool bPrefix = ValidTabName( aStrTable );
438     OSL_ENSURE(bPrefix, "Invalid Table Name");
439     SCTAB nDummy;
440     SCTAB i = static_cast<SCTAB>(maTabs.size())+1;
441 
442     for (SCTAB j = 0; j < nCount; ++j)
443     {
444         bool bOk = false;
445         while(!bOk)
446         {
447             rName = aStrTable;
448             rName.append(static_cast<sal_Int32>(i));
449             if (bPrefix)
450                 bOk = ValidNewTabName( rName.toString() );
451             else
452                 bOk = !GetTable( rName.toString(), nDummy );
453             i++;
454         }
455         aNames.push_back(rName.makeStringAndClear());
456     }
457 }
458 
459 void ScDocument::AppendTabOnLoad(const OUString& rName)
460 {
461     SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
462     if (!ValidTab(nTabCount))
463         // max table count reached.  No more tables.
464         return;
465 
466     OUString aName = rName;
467     CreateValidTabName(aName);
468     maTabs.emplace_back( new ScTable(this, nTabCount, aName) );
469 }
470 
471 void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
472 {
473     if (!ValidTab(nTab) || static_cast<SCTAB>(maTabs.size()) <= nTab)
474         return;
475 
476     if (!ValidTabName(rName))
477         return;
478 
479     maTabs[nTab]->SetName(rName);
480 }
481 
482 void ScDocument::InvalidateStreamOnSave()
483 {
484     for (const auto& a : maTabs)
485     {
486         if (a)
487             a->SetStreamValid(false);
488     }
489 }
490 
491 bool ScDocument::InsertTab(
492     SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
493 {
494     SCTAB   nTabCount = static_cast<SCTAB>(maTabs.size());
495     bool    bValid = ValidTab(nTabCount);
496     if ( !bExternalDocument )   // else test rName == "'Doc'!Tab" first
497         bValid = (bValid && ValidNewTabName(rName));
498     if (bValid)
499     {
500         if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
501         {
502             nPos = maTabs.size();
503             maTabs.emplace_back( new ScTable(this, nTabCount, rName) );
504             if ( bExternalDocument )
505                 maTabs[nTabCount]->SetVisible( false );
506         }
507         else
508         {
509             if (ValidTab(nPos) && (nPos < nTabCount))
510             {
511                 sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);
512 
513                 ScRange aRange( 0,0,nPos, MAXCOL,MAXROW,MAXTAB );
514                 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
515                 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
516                 if (pRangeName)
517                     pRangeName->UpdateInsertTab(aCxt);
518                 pDBCollection->UpdateReference(
519                                     URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 );
520                 if (pDPCollection)
521                     pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
522                 if (pDetOpList)
523                     pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
524                 UpdateChartRef( URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 );
525                 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
526                 if ( pUnoBroadcaster )
527                     pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
528 
529                 for (const auto& a : maTabs)
530                 {
531                     if (a)
532                         a->UpdateInsertTab(aCxt);
533                 }
534                 maTabs.emplace(maTabs.begin() + nPos, new ScTable(this, nPos, rName));
535 
536                 // UpdateBroadcastAreas must be called between UpdateInsertTab,
537                 // which ends listening, and StartAllListeners, to not modify
538                 // areas that are to be inserted by starting listeners.
539                 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
540                 for (const auto& a : maTabs)
541                 {
542                     if (a)
543                         a->UpdateCompile();
544                 }
545 
546                 StartAllListeners();
547 
548                 if (pValidationList)
549                 {
550                     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
551                     pValidationList->UpdateInsertTab(aCxt);
552                 }
553 
554                 bValid = true;
555             }
556             else
557                 bValid = false;
558         }
559     }
560 
561     if (bValid)
562     {
563         sc::SetFormulaDirtyContext aCxt;
564         aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
565         aCxt.mnTabDeletedStart = nPos;
566         aCxt.mnTabDeletedEnd = nPos;
567         SetAllFormulasDirty(aCxt);
568 
569         if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
570         {
571             SfxViewShell* pViewShell = SfxViewShell::GetFirst();
572             while (pViewShell)
573             {
574                 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, "");
575                 pViewShell = SfxViewShell::GetNext(*pViewShell);
576             }
577         }
578     }
579 
580     return bValid;
581 }
582 
583 bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
584             bool bNamesValid )
585 {
586     SCTAB   nNewSheets = static_cast<SCTAB>(rNames.size());
587     SCTAB    nTabCount = static_cast<SCTAB>(maTabs.size());
588     bool    bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);
589 
590     if (bValid)
591     {
592         if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
593         {
594             for ( SCTAB i = 0; i < nNewSheets; ++i )
595             {
596                 maTabs.emplace_back( new ScTable(this, nTabCount + i, rNames.at(i)) );
597             }
598         }
599         else
600         {
601             if (ValidTab(nPos) && (nPos < nTabCount))
602             {
603                 sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
604                 ScRange aRange( 0,0,nPos, MAXCOL,MAXROW,MAXTAB );
605                 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
606                 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
607                 if (pRangeName)
608                     pRangeName->UpdateInsertTab(aCxt);
609                 pDBCollection->UpdateReference(
610                                     URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,nNewSheets );
611                 if (pDPCollection)
612                     pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
613                 if (pDetOpList)
614                     pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets );
615                 UpdateChartRef( URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,nNewSheets );
616                 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
617                 if ( pUnoBroadcaster )
618                     pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );
619 
620                 for (const auto& a : maTabs)
621                 {
622                     if (a)
623                         a->UpdateInsertTab(aCxt);
624                 }
625                 for (SCTAB i = 0; i < nNewSheets; ++i)
626                 {
627                     maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(this, nPos + i, rNames.at(i)) );
628                 }
629 
630                 // UpdateBroadcastAreas must be called between UpdateInsertTab,
631                 // which ends listening, and StartAllListeners, to not modify
632                 // areas that are to be inserted by starting listeners.
633                 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
634                 for (const auto& a : maTabs)
635                 {
636                     if (a)
637                         a->UpdateCompile();
638                 }
639 
640                 StartAllListeners();
641 
642                 if (pValidationList)
643                 {
644                     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
645                     pValidationList->UpdateInsertTab(aCxt);
646                 }
647 
648                 bValid = true;
649             }
650             else
651                 bValid = false;
652         }
653     }
654 
655     if (bValid)
656     {
657         sc::SetFormulaDirtyContext aCxt;
658         SetAllFormulasDirty(aCxt);
659     }
660 
661     return bValid;
662 }
663 
664 bool ScDocument::DeleteTab( SCTAB nTab )
665 {
666     bool bValid = false;
667     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
668     {
669         if (maTabs[nTab])
670         {
671             SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
672             if (nTabCount > 1)
673             {
674                 sc::AutoCalcSwitch aACSwitch(*this, false);
675                 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
676 
677                 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab );
678                 DelBroadcastAreasInRange( aRange );
679 
680                 // #i8180# remove database ranges etc. that are on the deleted tab
681                 // (restored in undo with ScRefUndoData)
682 
683                 xColNameRanges->DeleteOnTab( nTab );
684                 xRowNameRanges->DeleteOnTab( nTab );
685                 pDBCollection->DeleteOnTab( nTab );
686                 if (pDPCollection)
687                     pDPCollection->DeleteOnTab( nTab );
688                 if (pDetOpList)
689                     pDetOpList->DeleteOnTab( nTab );
690                 DeleteAreaLinksOnTab( nTab );
691 
692                 // normal reference update
693 
694                 aRange.aEnd.SetTab( static_cast<SCTAB>(maTabs.size())-1 );
695                 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
696                 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
697                 if (pRangeName)
698                     pRangeName->UpdateDeleteTab(aCxt);
699                 pDBCollection->UpdateReference(
700                                     URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 );
701                 if (pDPCollection)
702                     pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
703                 if (pDetOpList)
704                     pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 );
705                 UpdateChartRef( URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 );
706                 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
707                 if (pValidationList)
708                 {
709                     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
710                     pValidationList->UpdateDeleteTab(aCxt);
711                 }
712                 if ( pUnoBroadcaster )
713                     pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );
714 
715                 for (auto & pTab : maTabs)
716                     if (pTab)
717                         pTab->UpdateDeleteTab(aCxt);
718 
719                 maTabs.erase(maTabs.begin() + nTab);
720                 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
721                 // which ends listening, and StartAllListeners, to not modify
722                 // areas that are to be inserted by starting listeners.
723                 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
724                 for (const auto& a : maTabs)
725                 {
726                     if (a)
727                         a->UpdateCompile();
728                 }
729                 // Excel-Filter deletes some Tables while loading, Listeners will
730                 // only be triggered after the loading is done.
731                 if ( !bInsertingFromOtherDoc )
732                 {
733                     StartAllListeners();
734 
735                     sc::SetFormulaDirtyContext aFormulaDirtyCxt;
736                     SetAllFormulasDirty(aFormulaDirtyCxt);
737                 }
738 
739                 if (comphelper::LibreOfficeKit::isActive())
740                 {
741                     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
742                     while (pViewShell)
743                     {
744                         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, "");
745                         pViewShell = SfxViewShell::GetNext(*pViewShell);
746                     }
747                 }
748 
749                 bValid = true;
750             }
751         }
752     }
753     return bValid;
754 }
755 
756 bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
757 {
758     bool bValid = false;
759     if (ValidTab(nTab) && (nTab + nSheets) <= static_cast<SCTAB>(maTabs.size()))
760     {
761         if (maTabs[nTab])
762         {
763             SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
764             if (nTabCount > nSheets)
765             {
766                 sc::AutoCalcSwitch aACSwitch(*this, false);
767                 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
768 
769                 for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
770                 {
771                     ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab + aTab );
772                     DelBroadcastAreasInRange( aRange );
773 
774                     // #i8180# remove database ranges etc. that are on the deleted tab
775                     // (restored in undo with ScRefUndoData)
776 
777                     xColNameRanges->DeleteOnTab( nTab + aTab );
778                     xRowNameRanges->DeleteOnTab( nTab + aTab );
779                     pDBCollection->DeleteOnTab( nTab + aTab );
780                     if (pDPCollection)
781                         pDPCollection->DeleteOnTab( nTab + aTab );
782                     if (pDetOpList)
783                         pDetOpList->DeleteOnTab( nTab + aTab );
784                     DeleteAreaLinksOnTab( nTab + aTab );
785                 }
786 
787                 if (pRangeName)
788                     pRangeName->UpdateDeleteTab(aCxt);
789 
790                 // normal reference update
791 
792                 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTabCount - 1 );
793                 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
794                 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
795                 pDBCollection->UpdateReference(
796                                     URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1*nSheets );
797                 if (pDPCollection)
798                     pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
799                 if (pDetOpList)
800                     pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets );
801                 UpdateChartRef( URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1*nSheets );
802                 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
803                 if (pValidationList)
804                 {
805                     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
806                     pValidationList->UpdateDeleteTab(aCxt);
807                 }
808                 if ( pUnoBroadcaster )
809                     pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );
810 
811                 for (auto & pTab : maTabs)
812                     if (pTab)
813                         pTab->UpdateDeleteTab(aCxt);
814 
815                 maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
816                 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
817                 // which ends listening, and StartAllListeners, to not modify
818                 // areas that are to be inserted by starting listeners.
819                 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
820                 for (const auto& a : maTabs)
821                 {
822                     if (a)
823                         a->UpdateCompile();
824                 }
825                 // Excel-Filter deletes some Tables while loading, Listeners will
826                 // only be triggered after the loading is done.
827                 if ( !bInsertingFromOtherDoc )
828                 {
829                     StartAllListeners();
830 
831                     sc::SetFormulaDirtyContext aFormulaDirtyCxt;
832                     SetAllFormulasDirty(aFormulaDirtyCxt);
833                 }
834 
835                 if (comphelper::LibreOfficeKit::isActive())
836                 {
837                     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
838                     while (pViewShell)
839                     {
840                         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, "");
841                         pViewShell = SfxViewShell::GetNext(*pViewShell);
842                     }
843                 }
844 
845                 bValid = true;
846             }
847         }
848     }
849     return bValid;
850 }
851 
852 bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
853 {
854     bool bValid = false;
855     SCTAB i;
856     if (ValidTab(nTab))
857     {
858         if (maTabs[nTab])
859         {
860             if ( bExternalDocument )
861                 bValid = true;      // composed name
862             else
863                 bValid = ValidTabName(rName);
864             for (i=0; (i< static_cast<SCTAB>(maTabs.size())) && bValid; i++)
865                 if (maTabs[i] && (i != nTab))
866                 {
867                     OUString aOldName = maTabs[i]->GetName();
868                     bValid = !ScGlobal::GetpTransliteration()->isEqual( rName, aOldName );
869                 }
870             if (bValid)
871             {
872                 // #i75258# update charts before renaming, so they can get their live data objects.
873                 // Once the charts are live, the sheet can be renamed without problems.
874                 if ( pChartListenerCollection )
875                     pChartListenerCollection->UpdateChartsContainingTab( nTab );
876                 maTabs[nTab]->SetName(rName);
877 
878                 // If formulas refer to the renamed sheet, the TokenArray remains valid,
879                 // but the XML stream must be re-generated.
880                 for (const auto& a : maTabs)
881                 {
882                     if (a)
883                         a->SetStreamValid( false );
884                 }
885 
886                 if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
887                 {
888                     SfxViewShell* pViewShell = SfxViewShell::GetFirst();
889                     while (pViewShell)
890                     {
891                         pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, "");
892                         pViewShell = SfxViewShell::GetNext(*pViewShell);
893                     }
894                 }
895             }
896         }
897     }
898     return bValid;
899 }
900 
901 void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
902 {
903     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
904         if (maTabs[nTab])
905             maTabs[nTab]->SetVisible(bVisible);
906 }
907 
908 bool ScDocument::IsVisible( SCTAB nTab ) const
909 {
910     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
911         if (maTabs[nTab])
912             return maTabs[nTab]->IsVisible();
913 
914     return false;
915 }
916 
917 bool ScDocument::IsStreamValid( SCTAB nTab ) const
918 {
919     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
920         return maTabs[nTab]->IsStreamValid();
921 
922     return false;
923 }
924 
925 void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
926 {
927     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
928         maTabs[nTab]->SetStreamValid( bSet, bIgnoreLock );
929 }
930 
931 void ScDocument::LockStreamValid( bool bLock )
932 {
933     mbStreamValidLocked = bLock;
934 }
935 
936 bool ScDocument::IsPendingRowHeights( SCTAB nTab ) const
937 {
938     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
939         return maTabs[nTab]->IsPendingRowHeights();
940 
941     return false;
942 }
943 
944 void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet )
945 {
946     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
947         maTabs[nTab]->SetPendingRowHeights( bSet );
948 }
949 
950 void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL )
951 {
952     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
953     {
954         if ( bImportingXML )
955         {
956             // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
957             // is applied in SetImportingXML(false). This is so the shapes can be loaded in
958             // normal LTR mode.
959 
960             maTabs[nTab]->SetLoadingRTL( bRTL );
961             return;
962         }
963 
964         maTabs[nTab]->SetLayoutRTL( bRTL );     // only sets the flag
965         maTabs[nTab]->SetDrawPageSize();
966 
967         //  mirror existing objects:
968 
969         if (mpDrawLayer)
970         {
971             SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
972             OSL_ENSURE(pPage,"Page ?");
973             if (pPage)
974             {
975                 SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
976                 SdrObject* pObject = aIter.Next();
977                 while (pObject)
978                 {
979                     //  objects with ScDrawObjData are re-positioned in SetPageSize,
980                     //  don't mirror again
981                     ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject );
982                     if ( !pData )
983                         mpDrawLayer->MirrorRTL( pObject );
984 
985                     pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
986 
987                     pObject = aIter.Next();
988                 }
989             }
990         }
991     }
992 }
993 
994 bool ScDocument::IsLayoutRTL( SCTAB nTab ) const
995 {
996     if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
997         return maTabs[nTab]->IsLayoutRTL();
998 
999     return false;
1000 }
1001 
1002 bool ScDocument::IsNegativePage( SCTAB nTab ) const
1003 {
1004     //  Negative page area is always used for RTL layout.
1005     //  The separate method is used to find all RTL handling of drawing objects.
1006     return IsLayoutRTL( nTab );
1007 }
1008 
1009 /* ----------------------------------------------------------------------------
1010     used search area:
1011 
1012     GetCellArea  - Only Data
1013     GetTableArea - Data / Attributes
1014     GetPrintArea - intended for character objects,
1015                     sweeps attributes all the way to bottom / right
1016 ---------------------------------------------------------------------------- */
1017 
1018 bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
1019 {
1020     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1021         if (maTabs[nTab])
1022             return maTabs[nTab]->GetCellArea( rEndCol, rEndRow );
1023 
1024     rEndCol = 0;
1025     rEndRow = 0;
1026     return false;
1027 }
1028 
1029 bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
1030 {
1031     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1032         if (maTabs[nTab])
1033             return maTabs[nTab]->GetTableArea( rEndCol, rEndRow );
1034 
1035     rEndCol = 0;
1036     rEndRow = 0;
1037     return false;
1038 }
1039 
1040 bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
1041 {
1042     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
1043         return false;
1044 
1045     SCCOL nCol1, nCol2;
1046     SCROW nRow1, nRow2;
1047     maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
1048     maTabs[nTab]->GetLastDataPos(nCol2, nRow2);
1049 
1050     if (nCol1 > nCol2 || nRow1 > nRow2)
1051         // invalid range.
1052         return false;
1053 
1054     // Make sure the area only shrinks, and doesn't grow.
1055     if (rStartCol < nCol1)
1056         rStartCol = nCol1;
1057     if (nCol2 < rEndCol)
1058         rEndCol = nCol2;
1059     if (rStartRow < nRow1)
1060         rStartRow = nRow1;
1061     if (nRow2 < rEndRow)
1062         rEndRow = nRow2;
1063 
1064     if (rStartCol > rEndCol || rStartRow > rEndRow)
1065         // invalid range.
1066         return false;
1067 
1068     return true;  // success!
1069 }
1070 
1071 bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
1072         SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
1073         bool bStickyTopRow, bool bStickyLeftCol, bool bConsiderCellNotes,
1074         bool bConsiderCellDrawObjects ) const
1075 {
1076     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
1077     {
1078         o_bShrunk = false;
1079         return false;
1080     }
1081     return maTabs[nTab]->ShrinkToUsedDataArea( o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow,
1082             bColumnsOnly, bStickyTopRow, bStickyLeftCol, bConsiderCellNotes, bConsiderCellDrawObjects );
1083 }
1084 
1085 SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
1086 {
1087     const ScTable* pTab = FetchTable(nTab);
1088     if (!pTab)
1089         return -1;
1090 
1091     return pTab->GetLastDataRow(nCol1, nCol2, nLastRow);
1092 }
1093 
1094 // connected area
1095 
1096 void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1097                               SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
1098 {
1099     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1100         maTabs[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
1101 }
1102 
1103 bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const
1104 {
1105     SCTAB nTab = rRange.aStart.Tab();
1106     if (nTab != rRange.aEnd.Tab())
1107         return true;
1108 
1109     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1110         return maTabs[nTab]->GetDataAreaSubrange(rRange);
1111 
1112     return true;
1113 }
1114 
1115 void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1116                                     SCCOL& rEndCol, SCROW& rEndRow )
1117 {
1118     if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1119         if (maTabs[nTab])
1120             maTabs[nTab]->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow );
1121 }
1122 
1123 void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList )
1124 {
1125     ScRangeListRef aNew = new ScRangeList;
1126     if (rRangeList.is())
1127     {
1128         for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
1129         {
1130             ScRange aRange( (*rRangeList)[i] );
1131             if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL ) ||
1132                  ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MAXROW ) )
1133             {
1134                 SCCOL nStartCol = aRange.aStart.Col();
1135                 SCROW nStartRow = aRange.aStart.Row();
1136                 SCCOL nEndCol = aRange.aEnd.Col();
1137                 SCROW nEndRow = aRange.aEnd.Row();
1138                 SCTAB nTab = aRange.aStart.Tab();
1139                 if ( nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1140                     maTabs[nTab]->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
1141                 aRange.aStart.SetCol( nStartCol );
1142                 aRange.aStart.SetRow( nStartRow );
1143                 aRange.aEnd.SetCol( nEndCol );
1144                 aRange.aEnd.SetRow( nEndRow );
1145             }
1146             aNew->push_back(aRange);
1147         }
1148     }
1149     else
1150     {
1151         OSL_FAIL("LimitChartIfAll: Ref==0");
1152     }
1153     rRangeList = aNew;
1154 }
1155 
1156 static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1157 {
1158     // without ScMarkData, leave start/end unchanged
1159     if ( pTabMark )
1160     {
1161         for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
1162             if (pTabMark->GetTableSelect(nTab))
1163             {
1164                 // find first range of consecutive selected sheets
1165                 rTabRangeStart = pTabMark->GetFirstSelected();
1166                 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1167                     ++nTab;
1168                 rTabRangeEnd = nTab;
1169                 return;
1170             }
1171     }
1172 }
1173 
1174 static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1175 {
1176     if ( pTabMark )
1177     {
1178         // find next range of consecutive selected sheets after rTabRangeEnd
1179         for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
1180             if (pTabMark->GetTableSelect(nTab))
1181             {
1182                 rTabRangeStart = nTab;
1183                 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1184                     ++nTab;
1185                 rTabRangeEnd = nTab;
1186                 return true;
1187             }
1188     }
1189     return false;
1190 }
1191 
1192 bool ScDocument::CanInsertRow( const ScRange& rRange ) const
1193 {
1194     SCCOL nStartCol = rRange.aStart.Col();
1195     SCROW nStartRow = rRange.aStart.Row();
1196     SCTAB nStartTab = rRange.aStart.Tab();
1197     SCCOL nEndCol = rRange.aEnd.Col();
1198     SCROW nEndRow = rRange.aEnd.Row();
1199     SCTAB nEndTab = rRange.aEnd.Tab();
1200     PutInOrder( nStartCol, nEndCol );
1201     PutInOrder( nStartRow, nEndRow );
1202     PutInOrder( nStartTab, nEndTab );
1203     SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
1204 
1205     bool bTest = true;
1206     for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1207         if (maTabs[i])
1208             bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );
1209 
1210     return bTest;
1211 }
1212 
1213 namespace {
1214 
1215 struct SetDirtyIfPostponedHandler
1216 {
1217     void operator() (const ScTableUniquePtr & p)
1218     {
1219         if (p)
1220             p->SetDirtyIfPostponed();
1221     }
1222 };
1223 
1224 struct BroadcastRecalcOnRefMoveHandler
1225 {
1226     void operator() (const ScTableUniquePtr & p)
1227     {
1228         if (p)
1229             p->BroadcastRecalcOnRefMove();
1230     }
1231 
1232     explicit BroadcastRecalcOnRefMoveHandler( ScDocument* pDoc ) :
1233         aSwitch( *pDoc, false),
1234         aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
1235     {
1236     }
1237 
1238 private:
1239     sc::AutoCalcSwitch const aSwitch; // first for ctor/dtor order, destroy second
1240     ScBulkBroadcast const aBulk;      // second for ctor/dtor order, destroy first
1241 };
1242 
1243 }
1244 
1245 bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
1246                             SCCOL nEndCol,   SCTAB nEndTab,
1247                             SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
1248                             const ScMarkData* pTabMark )
1249 {
1250     SCTAB i;
1251 
1252     PutInOrder( nStartCol, nEndCol );
1253     PutInOrder( nStartTab, nEndTab );
1254     if ( pTabMark )
1255     {
1256         nStartTab = 0;
1257         nEndTab = static_cast<SCTAB>(maTabs.size()) -1;
1258     }
1259 
1260     bool bTest = true;
1261     bool bRet = false;
1262     bool bOldAutoCalc = GetAutoCalc();
1263     SetAutoCalc( false );   // avoid multiple calculations
1264     for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1265         if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1266             bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
1267     if (bTest)
1268     {
1269         // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
1270         // aren't shifted that would be rebuild at UpdateReference
1271 
1272         // handle chunks of consecutive selected sheets together
1273         SCTAB nTabRangeStart = nStartTab;
1274         SCTAB nTabRangeEnd = nEndTab;
1275         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1276         ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MAXROW, nTabRangeEnd);
1277         sc::EndListeningContext aEndListenCxt(*this);
1278 
1279         std::vector<ScAddress> aGroupPos;
1280         do
1281         {
1282             aShiftedRange.aStart.SetTab(nTabRangeStart);
1283             aShiftedRange.aEnd.SetTab(nTabRangeEnd);
1284 
1285             // Collect all formula groups that will get split by the shifting,
1286             // and end all their listening.  Record the position of the top
1287             // cell of the topmost group, and the position of the bottom cell
1288             // of the bottommost group.
1289             EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);
1290 
1291             UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
1292         }
1293         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1294 
1295         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1296 
1297         sc::RefUpdateContext aCxt(*this);
1298         aCxt.meMode = URM_INSDEL;
1299         aCxt.maRange = aShiftedRange;
1300         aCxt.mnRowDelta = nSize;
1301         do
1302         {
1303             aCxt.maRange.aStart.SetTab(nTabRangeStart);
1304             aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
1305             UpdateReference(aCxt, pRefUndoDoc, false);        // without drawing objects
1306         }
1307         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1308 
1309         // UpdateReference should have set "needs listening" flags to those
1310         // whose references have been modified.  We also need to set this flag
1311         // to those that were in the groups that got split by shifting.
1312         SetNeedsListeningGroups(aGroupPos);
1313 
1314         for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1315             if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1316                 maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
1317 
1318         //  UpdateRef for drawing layer must be after inserting,
1319         //  when the new row heights are known.
1320         for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1321             if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1322                 maTabs[i]->UpdateDrawRef( URM_INSDEL,
1323                             nStartCol, nStartRow, nStartTab, nEndCol, MAXROW, nEndTab,
1324                             0, static_cast<SCROW>(nSize), 0 );
1325 
1326         if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1327         {   // A new Listening is needed when references to deleted ranges are restored,
1328             // previous Listeners were removed in FormulaCell UpdateReference.
1329             StartAllListeners();
1330         }
1331         else
1332         {   // Listeners have been removed in UpdateReference
1333             StartNeededListeners();
1334 
1335             // At least all cells using range names pointing relative to the
1336             // moved range must be recalculated, and all cells marked postponed
1337             // dirty.
1338             for (const auto& a : maTabs)
1339             {
1340                 if (a)
1341                     a->SetDirtyIfPostponed();
1342             }
1343 
1344             std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this));
1345         }
1346         bRet = true;
1347     }
1348     SetAutoCalc( bOldAutoCalc );
1349     if ( bRet && pChartListenerCollection )
1350         pChartListenerCollection->UpdateDirtyCharts();
1351     return bRet;
1352 }
1353 
1354 bool ScDocument::InsertRow( const ScRange& rRange )
1355 {
1356     return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1357                       rRange.aEnd.Col(),   rRange.aEnd.Tab(),
1358                       rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1359 }
1360 
1361 void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
1362                             SCCOL nEndCol,   SCTAB nEndTab,
1363                             SCROW nStartRow, SCSIZE nSize,
1364                             ScDocument* pRefUndoDoc, bool* pUndoOutline,
1365                             const ScMarkData* pTabMark )
1366 {
1367     SCTAB i;
1368 
1369     PutInOrder( nStartCol, nEndCol );
1370     PutInOrder( nStartTab, nEndTab );
1371     if ( pTabMark )
1372     {
1373         nStartTab = 0;
1374         nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1375     }
1376 
1377     sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1378 
1379     // handle chunks of consecutive selected sheets together
1380     SCTAB nTabRangeStart = nStartTab;
1381     SCTAB nTabRangeEnd = nEndTab;
1382     lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1383     do
1384     {
1385         if ( ValidRow(nStartRow+nSize) )
1386         {
1387             DelBroadcastAreasInRange( ScRange(
1388                 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1389                 ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
1390             UpdateBroadcastAreas( URM_INSDEL, ScRange(
1391                 ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
1392                 ScAddress( nEndCol, MAXROW, nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
1393         }
1394         else
1395             DelBroadcastAreasInRange( ScRange(
1396                 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1397                 ScAddress( nEndCol, MAXROW, nTabRangeEnd ) ) );
1398     }
1399     while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1400 
1401     sc::RefUpdateContext aCxt(*this);
1402     const bool bLastRowIncluded = (nStartRow + nSize == MAXROWCOUNT && ValidRow(nStartRow));
1403     if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1404     {
1405         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1406         aCxt.meMode = URM_INSDEL;
1407         aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
1408         if (bLastRowIncluded)
1409         {
1410             // Last row is included, shift a virtually non-existent row in.
1411             aCxt.maRange = ScRange( nStartCol, MAXROWCOUNT, nTabRangeStart, nEndCol, MAXROWCOUNT, nTabRangeEnd);
1412         }
1413         else
1414         {
1415             aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MAXROW, nTabRangeEnd);
1416         }
1417         do
1418         {
1419             UpdateReference(aCxt, pRefUndoDoc, true, false);
1420         }
1421         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1422     }
1423 
1424     if (pUndoOutline)
1425         *pUndoOutline = false;
1426 
1427     // Keep track of the positions of all formula groups that have been joined
1428     // during row deletion.
1429     std::vector<ScAddress> aGroupPos;
1430 
1431     for ( i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1432         if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1433             maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
1434 
1435     // Newly joined groups have some of their members still listening.  We
1436     // need to make sure none of them are listening.
1437     EndListeningGroups(aGroupPos);
1438 
1439     // Mark all joined groups for group listening.
1440     SetNeedsListeningGroups(aGroupPos);
1441 
1442     if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1443     {
1444         // Listeners have been removed in UpdateReference
1445         StartNeededListeners();
1446 
1447         // At least all cells using range names pointing relative to the moved
1448         // range must be recalculated, and all cells marked postponed dirty.
1449         for (const auto& a : maTabs)
1450         {
1451             if (a)
1452                 a->SetDirtyIfPostponed();
1453         }
1454 
1455         std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this));
1456     }
1457 
1458     if (pChartListenerCollection)
1459         pChartListenerCollection->UpdateDirtyCharts();
1460 }
1461 
1462 void ScDocument::DeleteRow( const ScRange& rRange )
1463 {
1464     DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1465                rRange.aEnd.Col(),   rRange.aEnd.Tab(),
1466                rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1467 }
1468 
1469 bool ScDocument::CanInsertCol( const ScRange& rRange ) const
1470 {
1471     SCCOL nStartCol = rRange.aStart.Col();
1472     SCROW nStartRow = rRange.aStart.Row();
1473     SCTAB nStartTab = rRange.aStart.Tab();
1474     SCCOL nEndCol = rRange.aEnd.Col();
1475     SCROW nEndRow = rRange.aEnd.Row();
1476     SCTAB nEndTab = rRange.aEnd.Tab();
1477     PutInOrder( nStartCol, nEndCol );
1478     PutInOrder( nStartRow, nEndRow );
1479     PutInOrder( nStartTab, nEndTab );
1480     SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);
1481 
1482     bool bTest = true;
1483     for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1484         if (maTabs[i])
1485             bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1486 
1487     return bTest;
1488 }
1489 
1490 bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
1491                             SCROW nEndRow,   SCTAB nEndTab,
1492                             SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1493                             const ScMarkData* pTabMark )
1494 {
1495     SCTAB i;
1496 
1497     PutInOrder( nStartRow, nEndRow );
1498     PutInOrder( nStartTab, nEndTab );
1499     if ( pTabMark )
1500     {
1501         nStartTab = 0;
1502         nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1503     }
1504 
1505     bool bTest = true;
1506     bool bRet = false;
1507     bool bOldAutoCalc = GetAutoCalc();
1508     SetAutoCalc( false );   // avoid multiple calculations
1509     for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1510         if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1511             bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1512     if (bTest)
1513     {
1514         // handle chunks of consecutive selected sheets together
1515         SCTAB nTabRangeStart = nStartTab;
1516         SCTAB nTabRangeEnd = nEndTab;
1517         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1518         do
1519         {
1520             UpdateBroadcastAreas( URM_INSDEL, ScRange(
1521                 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1522                 ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
1523         }
1524         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1525 
1526         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1527 
1528         sc::RefUpdateContext aCxt(*this);
1529         aCxt.meMode = URM_INSDEL;
1530         aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MAXCOL, nEndRow, nTabRangeEnd);
1531         aCxt.mnColDelta = nSize;
1532         do
1533         {
1534             UpdateReference(aCxt, pRefUndoDoc, true, false);
1535         }
1536         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1537 
1538         for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1539             if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1540                 maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);
1541 
1542         if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1543         {   // A new Listening is needed when references to deleted ranges are restored,
1544             // previous Listeners were removed in FormulaCell UpdateReference.
1545             StartAllListeners();
1546         }
1547         else
1548         {
1549             // Listeners have been removed in UpdateReference
1550             StartNeededListeners();
1551             // At least all cells using range names pointing relative to the
1552             // moved range must be recalculated, and all cells marked postponed
1553             // dirty.
1554             std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
1555             // Cells containing functions such as CELL, COLUMN or ROW may have
1556             // changed their values on relocation. Broadcast them.
1557             std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this));
1558         }
1559         bRet = true;
1560     }
1561     SetAutoCalc( bOldAutoCalc );
1562     if ( bRet && pChartListenerCollection )
1563         pChartListenerCollection->UpdateDirtyCharts();
1564     return bRet;
1565 }
1566 
1567 bool ScDocument::InsertCol( const ScRange& rRange )
1568 {
1569     return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
1570                       rRange.aEnd.Row(),   rRange.aEnd.Tab(),
1571                       rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
1572 }
1573 
1574 void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
1575                                 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1576                                 bool* pUndoOutline, const ScMarkData* pTabMark )
1577 {
1578     SCTAB i;
1579 
1580     PutInOrder( nStartRow, nEndRow );
1581     PutInOrder( nStartTab, nEndTab );
1582     if ( pTabMark )
1583     {
1584         nStartTab = 0;
1585         nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1586     }
1587 
1588     sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1589 
1590     // handle chunks of consecutive selected sheets together
1591     SCTAB nTabRangeStart = nStartTab;
1592     SCTAB nTabRangeEnd = nEndTab;
1593     lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1594     do
1595     {
1596         if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
1597         {
1598             DelBroadcastAreasInRange( ScRange(
1599                 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1600                 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
1601             UpdateBroadcastAreas( URM_INSDEL, ScRange(
1602                 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
1603                 ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
1604         }
1605         else
1606             DelBroadcastAreasInRange( ScRange(
1607                 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1608                 ScAddress( MAXCOL, nEndRow, nTabRangeEnd ) ) );
1609     }
1610     while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1611 
1612     sc::RefUpdateContext aCxt(*this);
1613     const bool bLastColIncluded = (nStartCol + nSize == MAXCOLCOUNT && ValidCol(nStartCol));
1614     if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1615     {
1616         lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1617         aCxt.meMode = URM_INSDEL;
1618         aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
1619         if (bLastColIncluded)
1620         {
1621             // Last column is included, shift a virtually non-existent column in.
1622             aCxt.maRange = ScRange( MAXCOLCOUNT, nStartRow, nTabRangeStart, MAXCOLCOUNT, nEndRow, nTabRangeEnd);
1623         }
1624         else
1625         {
1626             aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
1627                     MAXCOL, nEndRow, nTabRangeEnd);
1628         }
1629         do
1630         {
1631             UpdateReference(aCxt, pRefUndoDoc, true, false);
1632         }
1633         while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1634     }
1635 
1636     if (pUndoOutline)
1637         *pUndoOutline = false;
1638 
1639     for (i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); ++i)
1640     {
1641         if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1642             maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
1643     }
1644 
1645     if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1646     {
1647         // Listeners have been removed in UpdateReference
1648         StartNeededListeners();
1649 
1650         // At least all cells using range names pointing relative to the moved
1651         // range must be recalculated, and all cells marked postponed dirty.
1652         for (const auto& a : maTabs)
1653         {
1654             if (a)
1655                 a->SetDirtyIfPostponed();
1656         }
1657 
1658         std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this));
1659     }
1660 
1661     if (pChartListenerCollection)
1662         pChartListenerCollection->UpdateDirtyCharts();
1663 }
1664 
1665 void ScDocument::DeleteCol( const ScRange& rRange )
1666 {
1667     DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(),
1668                rRange.aEnd.Row(),   rRange.aEnd.Tab(),
1669                rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
1670 }
1671 
1672 //  for Area-Links: Insert/delete cells, when the range is changed.
1673 //  (without Paint)
1674 
1675 static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew,
1676                             ScRange& rColRange, bool& rInsCol, bool& rDelCol,
1677                             ScRange& rRowRange, bool& rInsRow, bool& rDelRow )
1678 {
1679     OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" );
1680 
1681     rInsCol = rDelCol = rInsRow = rDelRow = false;
1682 
1683     SCCOL nStartX = rOld.aStart.Col();
1684     SCROW nStartY = rOld.aStart.Row();
1685     SCCOL nOldEndX = rOld.aEnd.Col();
1686     SCROW nOldEndY = rOld.aEnd.Row();
1687     SCCOL nNewEndX = rNew.aEnd.Col();
1688     SCROW nNewEndY = rNew.aEnd.Row();
1689     SCTAB nTab = rOld.aStart.Tab();
1690 
1691     // if more rows, columns are inserted/deleted at the old height.
1692     bool bGrowY = ( nNewEndY > nOldEndY );
1693     SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY;
1694     SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX;
1695 
1696     // Columns
1697 
1698     if ( nNewEndX > nOldEndX )          // Insert columns
1699     {
1700         rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab );
1701         rInsCol = true;
1702     }
1703     else if ( nNewEndX < nOldEndX )     // Delete columns
1704     {
1705         rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab );
1706         rDelCol = true;
1707     }
1708 
1709     // Rows
1710 
1711     if ( nNewEndY > nOldEndY )          // Insert rows
1712     {
1713         rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab );
1714         rInsRow = true;
1715     }
1716     else if ( nNewEndY < nOldEndY )     // Delete rows
1717     {
1718         rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab );
1719         rDelRow = true;
1720     }
1721 }
1722 
1723 bool ScDocument::HasPartOfMerged( const ScRange& rRange )
1724 {
1725     bool bPart = false;
1726     SCTAB nTab = rRange.aStart.Tab();
1727 
1728     SCCOL nStartX = rRange.aStart.Col();
1729     SCROW nStartY = rRange.aStart.Row();
1730     SCCOL nEndX = rRange.aEnd.Col();
1731     SCROW nEndY = rRange.aEnd.Row();
1732 
1733     if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab,
1734                         HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
1735     {
1736         ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
1737         ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab );
1738 
1739         bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() ||
1740                   nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() );
1741     }
1742     return bPart;
1743 }
1744 
1745 size_t ScDocument::GetFormulaHash( const ScAddress& rPos ) const
1746 {
1747     SCTAB nTab = rPos.Tab();
1748     if (!ValidTab(nTab) || static_cast<size_t>(nTab) >= maTabs.size() || !maTabs[nTab])
1749         return 0;
1750 
1751     return maTabs[nTab]->GetFormulaHash(rPos.Col(), rPos.Row());
1752 }
1753 
1754 ScFormulaVectorState ScDocument::GetFormulaVectorState( const ScAddress& rPos ) const
1755 {
1756     SCTAB nTab = rPos.Tab();
1757     if (!ValidTab(nTab) || static_cast<size_t>(nTab) >= maTabs.size() || !maTabs[nTab])
1758         return FormulaVectorUnknown;
1759 
1760     return maTabs[nTab]->GetFormulaVectorState(rPos.Col(), rPos.Row());
1761 }
1762 
1763 formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos )
1764 {
1765     SCTAB nTab = rPos.Tab();
1766     if (!TableExists(nTab))
1767         return formula::FormulaTokenRef();
1768 
1769     return maTabs[nTab]->ResolveStaticReference(rPos.Col(), rPos.Row());
1770 }
1771 
1772 formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange )
1773 {
1774     SCTAB nTab = rRange.aStart.Tab();
1775     if (nTab != rRange.aEnd.Tab() || !TableExists(nTab))
1776         return formula::FormulaTokenRef();
1777 
1778     return maTabs[nTab]->ResolveStaticReference(
1779         rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
1780 }
1781 
1782 formula::VectorRefArray ScDocument::FetchVectorRefArray( const ScAddress& rPos, SCROW nLength )
1783 {
1784     SCTAB nTab = rPos.Tab();
1785     if (!TableExists(nTab))
1786         return formula::VectorRefArray();
1787 
1788     return maTabs[nTab]->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1789 }
1790 
1791 #ifdef DBG_UTIL
1792 void ScDocument::AssertNoInterpretNeeded( const ScAddress& rPos, SCROW nLength )
1793 {
1794     SCTAB nTab = rPos.Tab();
1795     assert(TableExists(nTab));
1796     return maTabs[nTab]->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1797 }
1798 #endif
1799 
1800 void ScDocument::UnlockAdjustHeight()
1801 {
1802     assert(nAdjustHeightLock > 0);
1803     if(nAdjustHeightLock > 0)
1804         --nAdjustHeightLock;
1805 }
1806 
1807 bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength, const ScFormulaCellGroupRef& mxGroup )
1808 {
1809     SCTAB nTab = rPos.Tab();
1810     if (!TableExists(nTab))
1811         return false;
1812 
1813     return maTabs[nTab]->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup);
1814 }
1815 
1816 bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
1817 {
1818     if ( rOld == rNew )
1819         return true;
1820 
1821     bool bOk = true;
1822     bool bInsCol,bDelCol,bInsRow,bDelRow;
1823     ScRange aColRange,aRowRange;
1824     lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1825 
1826     if ( bInsCol && !CanInsertCol( aColRange ) )            // Cells at the edge ?
1827         bOk = false;
1828     if ( bInsRow && !CanInsertRow( aRowRange ) )            // Cells at the edge ?
1829         bOk = false;
1830 
1831     if ( bInsCol || bDelCol )
1832     {
1833         aColRange.aEnd.SetCol(MAXCOL);
1834         if ( HasPartOfMerged(aColRange) )
1835             bOk = false;
1836     }
1837     if ( bInsRow || bDelRow )
1838     {
1839         aRowRange.aEnd.SetRow(MAXROW);
1840         if ( HasPartOfMerged(aRowRange) )
1841             bOk = false;
1842     }
1843 
1844     return bOk;
1845 }
1846 
1847 void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
1848 {
1849     if (bClear)
1850         DeleteAreaTab( rOld, InsertDeleteFlags::ALL );
1851 
1852     bool bInsCol,bDelCol,bInsRow,bDelRow;
1853     ScRange aColRange,aRowRange;
1854     lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1855 
1856     if ( bInsCol )
1857         InsertCol( aColRange );         // First insert columns
1858     if ( bInsRow )
1859         InsertRow( aRowRange );
1860 
1861     if ( bDelRow )
1862         DeleteRow( aRowRange );         // First delete rows
1863     if ( bDelCol )
1864         DeleteCol( aColRange );
1865 
1866     // Expand references to inserted rows
1867 
1868     if ( bInsCol || bInsRow )
1869     {
1870         ScRange aGrowSource = rOld;
1871         aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
1872         aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
1873         SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
1874         SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
1875         UpdateGrow( aGrowSource, nGrowX, nGrowY );
1876     }
1877 }
1878 
1879 void ScDocument::DeleteArea(
1880     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
1881     InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
1882 {
1883     sc::AutoCalcSwitch aACSwitch(*this, false);
1884 
1885     PutInOrder( nCol1, nCol2 );
1886     PutInOrder( nRow1, nRow2 );
1887 
1888     std::vector<ScAddress> aGroupPos;
1889     // Destroy and reconstruct listeners only if content is affected.
1890     bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
1891     if (bDelContent)
1892     {
1893         // Record the positions of top and/or bottom formula groups that intersect
1894         // the area borders.
1895         sc::EndListeningContext aCxt(*this);
1896         ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1897         for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1898         {
1899             if (rMark.GetTableSelect(i))
1900             {
1901                 aRange.aStart.SetTab(i);
1902                 aRange.aEnd.SetTab(i);
1903 
1904                 EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
1905             }
1906         }
1907         aCxt.purgeEmptyBroadcasters();
1908     }
1909 
1910     for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1911         if (maTabs[i])
1912             if ( rMark.GetTableSelect(i) || bIsUndo )
1913                 maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
1914 
1915     if (bDelContent)
1916     {
1917         // Re-start listeners on those top bottom groups that have been split.
1918         SetNeedsListeningGroups(aGroupPos);
1919         StartNeededListeners();
1920 
1921         // If formula groups were split their listeners were destroyed and may
1922         // need to be notified now that they're restored, ScTable::DeleteArea()
1923         // couldn't do that.
1924         if (!aGroupPos.empty())
1925         {
1926             ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1927             for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1928             {
1929                 if (rMark.GetTableSelect(i))
1930                 {
1931                     aRange.aStart.SetTab(i);
1932                     aRange.aEnd.SetTab(i);
1933                     SetDirty( aRange, true);
1934                 }
1935             }
1936         }
1937     }
1938 }
1939 
1940 void ScDocument::DeleteAreaTab(SCCOL nCol1, SCROW nRow1,
1941                                 SCCOL nCol2, SCROW nRow2,
1942                                 SCTAB nTab, InsertDeleteFlags nDelFlag)
1943 {
1944     PutInOrder( nCol1, nCol2 );
1945     PutInOrder( nRow1, nRow2 );
1946     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
1947     {
1948         bool bOldAutoCalc = GetAutoCalc();
1949         SetAutoCalc( false );   // avoid multiple calculations
1950         maTabs[nTab]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag);
1951         SetAutoCalc( bOldAutoCalc );
1952     }
1953 }
1954 
1955 void ScDocument::DeleteAreaTab( const ScRange& rRange, InsertDeleteFlags nDelFlag )
1956 {
1957     for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ )
1958         DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(),
1959                        rRange.aEnd.Col(),   rRange.aEnd.Row(),
1960                        nTab, nDelFlag );
1961 }
1962 
1963 void ScDocument::InitUndoSelected( const ScDocument* pSrcDoc, const ScMarkData& rTabSelection,
1964                                 bool bColInfo, bool bRowInfo )
1965 {
1966     if (bIsUndo)
1967     {
1968         Clear();
1969 
1970         SharePooledResources(pSrcDoc);
1971 
1972         for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++)
1973             if ( rTabSelection.GetTableSelect( nTab ) )
1974             {
1975                 ScTableUniquePtr pTable(new ScTable(this, nTab, OUString(), bColInfo, bRowInfo));
1976                 if (nTab < static_cast<SCTAB>(maTabs.size()))
1977                     maTabs[nTab] = std::move(pTable);
1978                 else
1979                     maTabs.push_back(std::move(pTable));
1980             }
1981             else
1982             {
1983                 if (nTab < static_cast<SCTAB>(maTabs.size()))
1984                     maTabs[nTab]=nullptr;
1985                 else
1986                     maTabs.push_back(nullptr);
1987             }
1988     }
1989     else
1990     {
1991         OSL_FAIL("InitUndo");
1992     }
1993 }
1994 
1995 void ScDocument::InitUndo( const ScDocument* pSrcDoc, SCTAB nTab1, SCTAB nTab2,
1996                                 bool bColInfo, bool bRowInfo )
1997 {
1998     if (!bIsUndo)
1999     {
2000         OSL_FAIL("InitUndo");
2001         return;
2002     }
2003 
2004     Clear();
2005 
2006     // Undo document shares its pooled resources with the source document.
2007     SharePooledResources(pSrcDoc);
2008 
2009     if (pSrcDoc->mpShell->GetMedium())
2010         maFileURL = pSrcDoc->mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
2011 
2012     if ( nTab2 >= static_cast<SCTAB>(maTabs.size()))
2013         maTabs.resize(nTab2 + 1);
2014     for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
2015     {
2016         maTabs[nTab].reset(new ScTable(this, nTab, OUString(), bColInfo, bRowInfo));
2017     }
2018 }
2019 
2020 void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo )
2021 {
2022     if (!bIsUndo)
2023     {
2024         OSL_FAIL("AddUndoTab");
2025         return;
2026     }
2027 
2028     if (nTab2 >= static_cast<SCTAB>(maTabs.size()))
2029     {
2030         maTabs.resize(nTab2+1);
2031     }
2032 
2033     for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
2034         if (!maTabs[nTab])
2035         {
2036             maTabs[nTab].reset( new ScTable(this, nTab, OUString(), bColInfo, bRowInfo) );
2037         }
2038 }
2039 
2040 void ScDocument::SetCutMode( bool bVal )
2041 {
2042     if (bIsClip)
2043         GetClipParam().mbCutMode = bVal;
2044     else
2045     {
2046         OSL_FAIL("SetCutMode without bIsClip");
2047     }
2048 }
2049 
2050 bool ScDocument::IsCutMode()
2051 {
2052     if (bIsClip)
2053         return GetClipParam().mbCutMode;
2054     else
2055     {
2056         OSL_FAIL("IsCutMode without bIsClip");
2057         return false;
2058     }
2059 }
2060 
2061 void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
2062                             SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2063                             InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2064                             const ScMarkData* pMarks, bool bColRowFlags )
2065 {
2066     PutInOrder( nCol1, nCol2 );
2067     PutInOrder( nRow1, nRow2 );
2068     PutInOrder( nTab1, nTab2 );
2069     if (rDestDoc.aDocName.isEmpty())
2070         rDestDoc.aDocName = aDocName;
2071     if (ValidTab(nTab1) && ValidTab(nTab2))
2072     {
2073         sc::CopyToDocContext aCxt(rDestDoc);
2074         bool bOldAutoCalc = rDestDoc.GetAutoCalc();
2075         rDestDoc.SetAutoCalc( false );     // avoid multiple calculations
2076         SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
2077         for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
2078         {
2079             if (maTabs[i] && rDestDoc.maTabs[i])
2080                 maTabs[i]->CopyToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
2081                                       bOnlyMarked, rDestDoc.maTabs[i].get(), pMarks,
2082                                       false, bColRowFlags, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
2083         }
2084         rDestDoc.SetAutoCalc(bOldAutoCalc);
2085     }
2086 }
2087 
2088 void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
2089                             SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2090                             InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2091 {
2092     PutInOrder( nCol1, nCol2 );
2093     PutInOrder( nRow1, nRow2 );
2094     PutInOrder( nTab1, nTab2 );
2095     if (ValidTab(nTab1) && ValidTab(nTab2))
2096     {
2097         sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2098 
2099         if (nTab1 > 0)
2100             CopyToDocument(0, 0, 0, MAXCOL, MAXROW, nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2101 
2102         sc::CopyToDocContext aCxt(rDestDoc);
2103         assert( nTab2 < static_cast<SCTAB>(maTabs.size()) && nTab2 < static_cast<SCTAB>(rDestDoc.maTabs.size()));
2104         for (SCTAB i = nTab1; i <= nTab2; i++)
2105         {
2106             if (maTabs[i] && rDestDoc.maTabs[i])
2107                 maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
2108                                     bOnlyMarked, rDestDoc.maTabs[i].get());
2109         }
2110 
2111         if (nTab2 < MAXTAB)
2112             CopyToDocument(0, 0, nTab2+1, MAXCOL, MAXROW, MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc);
2113     }
2114 }
2115 
2116 void ScDocument::CopyToDocument(const ScRange& rRange,
2117                             InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2118                             const ScMarkData* pMarks, bool bColRowFlags)
2119 {
2120     ScRange aNewRange = rRange;
2121     aNewRange.PutInOrder();
2122 
2123     if (rDestDoc.aDocName.isEmpty())
2124         rDestDoc.aDocName = aDocName;
2125 
2126     sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2127 
2128     // tdf#102364 - in some pathological cases CopyToDocument() replacing cells with new cells
2129     // can lead to repetitive splitting and rejoining of the same formula group, which can get
2130     // quadratically expensive with large groups. So do the grouping just once at the end.
2131     sc::DelayFormulaGroupingSwitch delayGrouping( rDestDoc, true );
2132 
2133     sc::CopyToDocContext aCxt(rDestDoc);
2134     aCxt.setStartListening(false);
2135 
2136     SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
2137     for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++)
2138     {
2139         ScTable* pTab = FetchTable(i);
2140         ScTable* pDestTab = rDestDoc.FetchTable(i);
2141         if (!pTab || !pDestTab)
2142             continue;
2143 
2144         pTab->CopyToTable(
2145             aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2146             nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags,
2147             /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true);
2148     }
2149 
2150     delayGrouping.reset(); // groups need to be updated before setting up listeners
2151     rDestDoc.StartAllListeners(aNewRange);
2152 }
2153 
2154 void ScDocument::UndoToDocument(const ScRange& rRange,
2155                             InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2156 {
2157     sc::AutoCalcSwitch aAutoCalcSwitch(*this, false);
2158 
2159     ScRange aNewRange = rRange;
2160     aNewRange.PutInOrder();
2161     SCTAB nTab1 = aNewRange.aStart.Tab();
2162     SCTAB nTab2 = aNewRange.aEnd.Tab();
2163 
2164     sc::CopyToDocContext aCxt(rDestDoc);
2165     if (nTab1 > 0)
2166         CopyToDocument(0, 0, 0, MAXCOL, MAXROW, nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2167 
2168     SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
2169     for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
2170     {
2171         if (maTabs[i] && rDestDoc.maTabs[i])
2172             maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(),
2173                                     aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2174                                     nFlags, bOnlyMarked, rDestDoc.maTabs[i].get());
2175     }
2176 
2177     if (nTab2 < static_cast<SCTAB>(maTabs.size()))
2178         CopyToDocument(0, 0 , nTab2+1, MAXCOL, MAXROW, maTabs.size(), InsertDeleteFlags::FORMULA, false, rDestDoc);
2179 }
2180 
2181 void ScDocument::CopyToClip(const ScClipParam& rClipParam,
2182                             ScDocument* pClipDoc, const ScMarkData* pMarks,
2183                             bool bKeepScenarioFlags, bool bIncludeObjects )
2184 {
2185     OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" );
2186 
2187     if (bIsClip)
2188         return;
2189 
2190     if (!pClipDoc)
2191     {
2192         SAL_WARN("sc", "CopyToClip: no ClipDoc");
2193         pClipDoc = ScModule::GetClipDoc();
2194     }
2195 
2196     if (mpShell->GetMedium())
2197     {
2198         pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
2199         // for unsaved files use the title name and adjust during save of file
2200         if (pClipDoc->maFileURL.isEmpty())
2201             pClipDoc->maFileURL = mpShell->GetName();
2202     }
2203     else
2204     {
2205         pClipDoc->maFileURL = mpShell->GetName();
2206     }
2207 
2208     //init maTabNames
2209     for (const auto& rxTab : maTabs)
2210     {
2211         if( rxTab )
2212         {
2213             OUString aTabName = rxTab->GetName();
2214             pClipDoc->maTabNames.push_back(aTabName);
2215         }
2216         else
2217             pClipDoc->maTabNames.emplace_back();
2218     }
2219 
2220     pClipDoc->aDocName = aDocName;
2221     pClipDoc->SetClipParam(rClipParam);
2222     ScRange aClipRange = rClipParam.getWholeRange();
2223     SCTAB nEndTab =  static_cast<SCTAB>(maTabs.size());
2224 
2225     pClipDoc->ResetClip(this, pMarks);
2226 
2227     sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
2228     CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);
2229 
2230     for (SCTAB i = 0; i < nEndTab; ++i)
2231     {
2232         if (!maTabs[i] || i >= static_cast<SCTAB>(pClipDoc->maTabs.size()) || !pClipDoc->maTabs[i])
2233             continue;
2234 
2235         if ( pMarks && !pMarks->GetTableSelect(i) )
2236             continue;
2237 
2238         maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
2239 
2240         if (mpDrawLayer && bIncludeObjects)
2241         {
2242             //  also copy drawing objects
2243             tools::Rectangle aObjRect = GetMMRect(
2244                 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
2245             mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
2246         }
2247     }
2248 
2249     // Make sure to mark overlapped cells.
2250     pClipDoc->ExtendMerge(aClipRange, true);
2251 }
2252 
2253 void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument* pDestDoc)
2254 {
2255     if (!pDestDoc)
2256         return;
2257 
2258     ScTable* pSrcTab = rSrcRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr;
2259     ScTable* pDestTab = nDestTab < static_cast<SCTAB>(pDestDoc->maTabs.size()) ? pDestDoc->maTabs[nDestTab].get() : nullptr;
2260 
2261     if (!pSrcTab || !pDestTab)
2262         return;
2263 
2264     pDestDoc->GetFormatTable()->MergeFormatter(*GetFormatTable());
2265     SvNumberFormatterMergeMap aMap = pDestDoc->GetFormatTable()->ConvertMergeTableToMap();
2266 
2267     pSrcTab->CopyStaticToDocument(
2268         rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(),
2269         aMap, pDestTab);
2270 }
2271 
2272 void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc )
2273 {
2274     if (!TableExists(rSrcPos.Tab()) || !rDestDoc.TableExists(rDestPos.Tab()))
2275         return;
2276 
2277     ScTable& rSrcTab = *maTabs[rSrcPos.Tab()];
2278     ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()];
2279 
2280     rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab);
2281 }
2282 
2283 void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1,
2284                                 SCCOL nCol2, SCROW nRow2,
2285                                 SCTAB nTab, ScDocument* pClipDoc)
2286 {
2287     if (!bIsClip)
2288     {
2289         if (!pClipDoc)
2290         {
2291             SAL_WARN("sc", "CopyTabToClip: no ClipDoc");
2292             pClipDoc = ScModule::GetClipDoc();
2293         }
2294 
2295         if (mpShell->GetMedium())
2296         {
2297             pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
2298             // for unsaved files use the title name and adjust during save of file
2299             if (pClipDoc->maFileURL.isEmpty())
2300                 pClipDoc->maFileURL = mpShell->GetName();
2301         }
2302         else
2303         {
2304             pClipDoc->maFileURL = mpShell->GetName();
2305         }
2306 
2307         //init maTabNames
2308         for (const auto& rxTab : maTabs)
2309         {
2310             if( rxTab )
2311             {
2312                 OUString aTabName = rxTab->GetName();
2313                 pClipDoc->maTabNames.push_back(aTabName);
2314             }
2315             else
2316                 pClipDoc->maTabNames.emplace_back();
2317         }
2318 
2319         PutInOrder( nCol1, nCol2 );
2320         PutInOrder( nRow1, nRow2 );
2321 
2322         ScClipParam& rClipParam = pClipDoc->GetClipParam();
2323         pClipDoc->aDocName = aDocName;
2324         rClipParam.maRanges.RemoveAll();
2325         rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
2326         pClipDoc->ResetClip( this, nTab );
2327 
2328         sc::CopyToClipContext aCxt(*pClipDoc, false);
2329         if (nTab < static_cast<SCTAB>(maTabs.size()) && nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()))
2330             if (maTabs[nTab] && pClipDoc->maTabs[nTab])
2331                 maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get());
2332 
2333         pClipDoc->GetClipParam().mbCutMode = false;
2334     }
2335 }
2336 
2337 void ScDocument::TransposeClip( ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink )
2338 {
2339     OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip,
2340                     "TransposeClip with wrong Document" );
2341 
2342         // initialize
2343         // -> pTransClip has to be deleted before the original document!
2344 
2345     pTransClip->ResetClip(this, nullptr);     // all
2346 
2347         // Take over range
2348 
2349     if (pRangeName)
2350     {
2351         pTransClip->GetRangeName()->clear();
2352         for (const auto& rEntry : *pRangeName)
2353         {
2354             sal_uInt16 nIndex = rEntry.second->GetIndex();
2355             ScRangeData* pData = new ScRangeData(*rEntry.second);
2356             if (pTransClip->pRangeName->insert(pData))
2357                 pData->SetIndex(nIndex);
2358         }
2359     }
2360 
2361     // The data
2362 
2363     ScRange aClipRange = GetClipParam().getWholeRange();
2364     if ( ValidRow(aClipRange.aEnd.Row()-aClipRange.aStart.Row()) )
2365     {
2366         for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++)
2367             if (maTabs[i])
2368             {
2369                 OSL_ENSURE( pTransClip->maTabs[i], "TransposeClip: Table not there" );
2370                 maTabs[i]->TransposeClip( aClipRange.aStart.Col(), aClipRange.aStart.Row(),
2371                                             aClipRange.aEnd.Col(), aClipRange.aEnd.Row(),
2372                                             pTransClip->maTabs[i].get(), nFlags, bAsLink );
2373 
2374                 if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) )
2375                 {
2376                     //  Drawing objects are copied to the new area without transposing.
2377                     //  CopyFromClip is used to adjust the objects to the transposed block's
2378                     //  cell range area.
2379                     //  (mpDrawLayer in the original clipboard document is set only if there
2380                     //  are drawing objects to copy)
2381 
2382                     pTransClip->InitDrawLayer();
2383                     tools::Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(),
2384                                                         aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i );
2385                     tools::Rectangle aDestRect = pTransClip->GetMMRect( 0, 0,
2386                             static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()),
2387                             static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i );
2388                     pTransClip->mpDrawLayer->CopyFromClip( mpDrawLayer.get(), i, aSourceRect, ScAddress(0,0,i), aDestRect );
2389                 }
2390             }
2391 
2392         pTransClip->SetClipParam(GetClipParam());
2393         pTransClip->GetClipParam().transpose();
2394     }
2395     else
2396     {
2397         SAL_WARN("sc", "TransposeClip: Too big");
2398     }
2399 
2400     // This happens only when inserting...
2401 
2402     GetClipParam().mbCutMode = false;
2403 }
2404 
2405 namespace {
2406 
2407 void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName,
2408         const sc::UpdatedRangeNames::NameIndicesType& rUsedNames)
2409 {
2410     pClipRangeName->clear();
2411     for (const auto& rEntry : *pRangeName)        //TODO: also DB and Pivot regions!!!
2412     {
2413         sal_uInt16 nIndex = rEntry.second->GetIndex();
2414         bool bInUse = (rUsedNames.count(nIndex) > 0);
2415         if (!bInUse)
2416             continue;
2417 
2418         ScRangeData* pData = new ScRangeData(*rEntry.second);
2419         if (pClipRangeName->insert(pData))
2420             pData->SetIndex(nIndex);
2421     }
2422 }
2423 
2424 }
2425 
2426 void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks)
2427 {
2428     if (!pRangeName || pRangeName->empty())
2429         return;
2430 
2431     sc::UpdatedRangeNames aUsedNames;        // indexes of named ranges that are used in the copied cells
2432     SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pClipDoc->maTabs.size()));
2433     for (SCTAB i = 0; i < nMinSizeBothTabs; ++i)
2434         if (maTabs[i] && pClipDoc->maTabs[i])
2435             if ( !pMarks || pMarks->GetTableSelect(i) )
2436                 maTabs[i]->FindRangeNamesInUse(
2437                     rClipRange.aStart.Col(), rClipRange.aStart.Row(),
2438                     rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames);
2439 
2440     /* TODO: handle also sheet-local names */
2441     sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1));
2442     copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames);
2443 }
2444 
2445 ScDocument::NumFmtMergeHandler::NumFmtMergeHandler(ScDocument* pDoc, const ScDocument* pSrcDoc) :
2446         mpDoc(pDoc)
2447 {
2448     mpDoc->MergeNumberFormatter(pSrcDoc);
2449 }
2450 
2451 ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler()
2452 {
2453     ScMutationGuard aGuard(mpDoc, ScMutationGuardFlags::CORE);
2454     mpDoc->pFormatExchangeList = nullptr;
2455 }
2456 
2457 void ScDocument::PrepareFormulaCalc()
2458 {
2459     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
2460     mpFormulaGroupCxt.reset();
2461 }
2462 
2463 SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos )
2464 {
2465     ScTable* pTab = FetchTable(rPos.Tab());
2466     if (!pTab)
2467         return nullptr;
2468 
2469     return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2470 }
2471 
2472 const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const
2473 {
2474     const ScTable* pTab = FetchTable(rPos.Tab());
2475     if (!pTab)
2476         return nullptr;
2477 
2478     return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2479 }
2480 
2481 void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength )
2482 {
2483     ScTable* pTab = FetchTable(rTopPos.Tab());
2484     if (!pTab || nLength <= 0)
2485         return;
2486 
2487     pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
2488 }
2489 
2490 #if DUMP_COLUMN_STORAGE
2491 void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
2492 {
2493     const ScTable* pTab = FetchTable(nTab);
2494     if (!pTab)
2495         return;
2496 
2497     pTab->DumpColumnStorage(nCol);
2498 }
2499 #endif
2500 
2501 #if DEBUG_AREA_BROADCASTER
2502 void ScDocument::DumpAreaBroadcasters() const
2503 {
2504     if (pBASM)
2505         pBASM->Dump();
2506 }
2507 #endif
2508 
2509 bool ScDocument::TableExists( SCTAB nTab ) const
2510 {
2511     return ValidTab(nTab) && static_cast<size_t>(nTab) < maTabs.size() && maTabs[nTab];
2512 }
2513 
2514 ScTable* ScDocument::FetchTable( SCTAB nTab )
2515 {
2516     if (!TableExists(nTab))
2517         return nullptr;
2518 
2519     return maTabs[nTab].get();
2520 }
2521 
2522 const ScTable* ScDocument::FetchTable( SCTAB nTab ) const
2523 {
2524     if (!TableExists(nTab))
2525         return nullptr;
2526 
2527     return maTabs[nTab].get();
2528 }
2529 
2530 ScColumnsRange ScDocument::GetColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
2531 {
2532     if (!TableExists(nTab))
2533     {
2534         std::vector<std::unique_ptr<ScColumn>> aEmptyVector;
2535         return ScColumnsRange(ScColumnsRange::Iterator(aEmptyVector.begin()),
2536                               ScColumnsRange::Iterator(aEmptyVector.end()));
2537     }
2538 
2539     return maTabs[nTab]->GetColumnsRange(nColBegin, nColEnd);
2540 }
2541 
2542 void ScDocument::MergeNumberFormatter(const ScDocument* pSrcDoc)
2543 {
2544     SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable();
2545     SvNumberFormatter* pOtherFormatter = pSrcDoc->mxPoolHelper->GetFormTable();
2546     if (pOtherFormatter && pOtherFormatter != pThisFormatter)
2547     {
2548         SvNumberFormatterIndexTable* pExchangeList =
2549                  pThisFormatter->MergeFormatter(*pOtherFormatter);
2550         if (!pExchangeList->empty())
2551         {
2552             ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
2553             pFormatExchangeList = pExchangeList;
2554         }
2555     }
2556 }
2557 
2558 ScClipParam& ScDocument::GetClipParam()
2559 {
2560     if (!mpClipParam)
2561         mpClipParam.reset(new ScClipParam);
2562 
2563     return *mpClipParam;
2564 }
2565 
2566 void ScDocument::SetClipParam(const ScClipParam& rParam)
2567 {
2568     mpClipParam.reset(new ScClipParam(rParam));
2569 }
2570 
2571 bool ScDocument::IsClipboardSource() const
2572 {
2573     if (bIsClip || mpShell == nullptr)
2574         return false;
2575 
2576     ScDocument* pClipDoc = ScModule::GetClipDoc();
2577     return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() &&
2578             mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
2579 }
2580 
2581 void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
2582                                         SCCOL nCol2, SCROW nRow2,
2583                                         const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
2584 {
2585     if (nInsFlag & InsertDeleteFlags::CONTENTS)
2586     {
2587         std::shared_ptr<sc::ColumnBlockPositionSet> pSet(
2588             new sc::ColumnBlockPositionSet(*this));
2589 
2590         sc::StartListeningContext aStartCxt(*this, pSet);
2591         sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
2592 
2593         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
2594         for (const auto& rTab : rMark)
2595         {
2596             if (rTab >= nMax)
2597                 break;
2598             if (maTabs[rTab])
2599                 maTabs[rTab]->StartListeningFormulaCells(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
2600         }
2601     }
2602 }
2603 
2604 void ScDocument::SetDirtyFromClip(
2605     SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
2606     InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
2607 {
2608     if (nInsFlag & InsertDeleteFlags::CONTENTS)
2609     {
2610         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
2611         for (const auto& rTab : rMark)
2612         {
2613             if (rTab >= nMax)
2614                 break;
2615             if (maTabs[rTab])
2616                 maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
2617         }
2618     }
2619 }
2620 
2621 bool ScDocument::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCTAB nTab, SCCOL nCol )
2622 {
2623     if (!TableExists(nTab))
2624         return false;
2625 
2626     return maTabs[nTab]->InitColumnBlockPosition(rBlockPos, nCol);
2627 }
2628 
2629 void ScDocument::CopyBlockFromClip(
2630     sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
2631     const ScMarkData& rMark, SCCOL nDx, SCROW nDy )
2632 {
2633     TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2634     SCTAB nTabEnd = rCxt.getTabEnd();
2635     SCTAB nClipTab = 0;
2636     for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
2637     {
2638         if (maTabs[i] && rMark.GetTableSelect(i) )
2639         {
2640             while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2641 
2642             maTabs[i]->CopyFromClip(
2643                 rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get());
2644 
2645             if (rCxt.getClipDoc()->mpDrawLayer && (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS))
2646             {
2647                 //  also copy drawing objects
2648 
2649                 // drawing layer must be created before calling CopyFromClip
2650                 // (ScDocShell::MakeDrawLayer also does InitItems etc.)
2651                 OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" );
2652                 if ( mpDrawLayer )
2653                 {
2654                     //  For GetMMRect, the row heights in the target document must already be valid
2655                     //  (copied in an extra step before pasting, or updated after pasting cells, but
2656                     //  before pasting objects).
2657 
2658                     tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect(
2659                                     nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab );
2660                     tools::Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i );
2661                     mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRect,
2662                                                 ScAddress( nCol1, nRow1, i ), aDestRect );
2663                 }
2664             }
2665 
2666             nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2667         }
2668     }
2669     if (rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS)
2670     {
2671         nClipTab = 0;
2672         for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
2673         {
2674             if (maTabs[i] && rMark.GetTableSelect(i) )
2675             {
2676                 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2677                 SCTAB nDz = i - nClipTab;
2678 
2679                 //  ranges of consecutive selected tables (in clipboard and dest. doc)
2680                 //  must be handled in one UpdateReference call
2681                 SCTAB nFollow = 0;
2682                 while ( i + nFollow < nTabEnd
2683                         && rMark.GetTableSelect( i + nFollow + 1 )
2684                         && nClipTab + nFollow < MAXTAB
2685                         && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
2686                     ++nFollow;
2687 
2688                 sc::RefUpdateContext aRefCxt(*this);
2689                 aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
2690                 aRefCxt.mnColDelta = nDx;
2691                 aRefCxt.mnRowDelta = nDy;
2692                 aRefCxt.mnTabDelta = nDz;
2693                 if (rCxt.getClipDoc()->GetClipParam().mbCutMode)
2694                 {
2695                     // Update references only if cut originates from the same
2696                     // document we are pasting into.
2697                     if (rCxt.getClipDoc()->GetPool() == GetPool())
2698                     {
2699                         bool bOldInserting = IsInsertingFromOtherDoc();
2700                         SetInsertingFromOtherDoc( true);
2701                         aRefCxt.meMode = URM_MOVE;
2702                         UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2703 
2704                         // For URM_MOVE group listeners may have been removed,
2705                         // re-establish them.
2706                         if (!aRefCxt.maRegroupCols.empty())
2707                         {
2708                             /* TODO: holding the ColumnSet in a shared_ptr at
2709                              * RefUpdateContext would eliminate the need of
2710                              * copying it here. */
2711                             std::shared_ptr<const sc::ColumnSet> pColSet( new sc::ColumnSet( aRefCxt.maRegroupCols));
2712                             StartNeededListeners( pColSet);
2713                         }
2714 
2715                         SetInsertingFromOtherDoc( bOldInserting);
2716                     }
2717                 }
2718                 else
2719                 {
2720                     aRefCxt.meMode = URM_COPY;
2721                     UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2722                 }
2723 
2724                 nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size());
2725                 i = sal::static_int_cast<SCTAB>( i + nFollow );
2726             }
2727         }
2728     }
2729 }
2730 
2731 void ScDocument::CopyNonFilteredFromClip(
2732     sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
2733     const ScMarkData& rMark, SCCOL nDx, SCROW & rClipStartRow )
2734 {
2735     //  call CopyBlockFromClip for ranges of consecutive non-filtered rows
2736     //  nCol1/nRow1 etc. is in target doc
2737 
2738     //  filtered state is taken from first used table in clipboard (as in GetClipArea)
2739     SCTAB nFlagTab = 0;
2740     TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2741     while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] )
2742         ++nFlagTab;
2743 
2744     SCROW nSourceRow = rClipStartRow;
2745     SCROW nSourceEnd = 0;
2746     if (!rCxt.getClipDoc()->GetClipParam().maRanges.empty())
2747         nSourceEnd = rCxt.getClipDoc()->GetClipParam().maRanges.front().aEnd.Row();
2748     SCROW nDestRow = nRow1;
2749 
2750     while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 )
2751     {
2752         // skip filtered rows
2753         nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab);
2754 
2755         if ( nSourceRow <= nSourceEnd )
2756         {
2757             // look for more non-filtered rows following
2758             SCROW nLastRow = nSourceRow;
2759             (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow);
2760             SCROW nFollow = nLastRow - nSourceRow;
2761 
2762             if (nFollow > nSourceEnd - nSourceRow)
2763                 nFollow = nSourceEnd - nSourceRow;
2764             if (nFollow > nRow2 - nDestRow)
2765                 nFollow = nRow2 - nDestRow;
2766 
2767             SCROW nNewDy = nDestRow - nSourceRow;
2768             CopyBlockFromClip(
2769                 rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy);
2770 
2771             nSourceRow += nFollow + 1;
2772             nDestRow += nFollow + 1;
2773         }
2774     }
2775     rClipStartRow = nSourceRow;
2776 }
2777 
2778 namespace {
2779 
2780 class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
2781 {
2782     ScDocument& mrDoc;
2783     ScColumn* mpCol;
2784 
2785 public:
2786     explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {}
2787 
2788     virtual void startColumn( ScColumn* pCol ) override
2789     {
2790         mpCol = pCol;
2791     }
2792 
2793     virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2794     {
2795         if (!bVal)
2796             return;
2797 
2798         assert(mpCol);
2799         ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
2800         aRange.aEnd.SetRow(nRow2);
2801         mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
2802     };
2803 };
2804 
2805 }
2806 
2807 void ScDocument::CopyFromClip( const ScRange& rDestRange, const ScMarkData& rMark,
2808                                 InsertDeleteFlags nInsFlag,
2809                                 ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut,
2810                                 bool bAsLink, bool bIncludeFiltered, bool bSkipAttrForEmpty,
2811                                 const ScRangeList * pDestRanges )
2812 {
2813     if (bIsClip)
2814         return;
2815 
2816     if (!pClipDoc)
2817     {
2818         OSL_FAIL("CopyFromClip: no ClipDoc");
2819         pClipDoc = ScModule::GetClipDoc();
2820     }
2821 
2822     if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
2823         return;
2824 
2825     sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc.
2826 
2827     NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc);
2828 
2829     SCCOL nAllCol1 = rDestRange.aStart.Col();
2830     SCROW nAllRow1 = rDestRange.aStart.Row();
2831     SCCOL nAllCol2 = rDestRange.aEnd.Col();
2832     SCROW nAllRow2 = rDestRange.aEnd.Row();
2833 
2834     SCCOL nXw = 0;
2835     SCROW nYw = 0;
2836     ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
2837     for (SCTAB nTab = 0; nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()); nTab++)    // find largest merge overlap
2838         if (pClipDoc->maTabs[nTab])                   // all sheets of the clipboard content
2839         {
2840             SCCOL nThisEndX = aClipRange.aEnd.Col();
2841             SCROW nThisEndY = aClipRange.aEnd.Row();
2842             pClipDoc->ExtendMerge( aClipRange.aStart.Col(),
2843                                     aClipRange.aStart.Row(),
2844                                     nThisEndX, nThisEndY, nTab );
2845             // only extra value from ExtendMerge
2846             nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() );
2847             nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() );
2848             if ( nThisEndX > nXw )
2849                 nXw = nThisEndX;
2850             if ( nThisEndY > nYw )
2851                 nYw = nThisEndY;
2852         }
2853 
2854     SCCOL nDestAddX;
2855     SCROW nDestAddY;
2856     pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered );
2857     nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX );
2858     nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY );   // ClipArea, plus ExtendMerge value
2859 
2860     /*  Decide which contents to delete before copying. Delete all
2861         contents if nInsFlag contains any real content flag.
2862         #i102056# Notes are pasted from clipboard in a second pass,
2863         together with the special flag InsertDeleteFlags::ADDNOTES that states to not
2864         overwrite/delete existing cells but to insert the notes into
2865         these cells. In this case, just delete old notes from the
2866         destination area. */
2867     InsertDeleteFlags nDelFlag = InsertDeleteFlags::NONE;
2868     if ( (nInsFlag & (InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ADDNOTES)) == (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES) )
2869         nDelFlag |= InsertDeleteFlags::NOTE;
2870     else if ( nInsFlag & InsertDeleteFlags::CONTENTS )
2871         nDelFlag |= InsertDeleteFlags::CONTENTS;
2872 
2873     if (nInsFlag & InsertDeleteFlags::ATTRIB)
2874         nDelFlag |= InsertDeleteFlags::ATTRIB;
2875 
2876     sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty);
2877     std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
2878     aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
2879     aCxt.setDeleteFlag(nDelFlag);
2880 
2881     ScRangeList aLocalRangeList;
2882     if (!pDestRanges)
2883     {
2884         aLocalRangeList.push_back( rDestRange);
2885         pDestRanges = &aLocalRangeList;
2886     }
2887 
2888     bInsertingFromOtherDoc = true;  // No Broadcast/Listener created at Insert
2889 
2890     sc::ColumnSpanSet aBroadcastSpans(false);
2891 
2892     SCCOL nClipStartCol = aClipRange.aStart.Col();
2893     SCROW nClipStartRow = aClipRange.aStart.Row();
2894     SCROW nClipEndRow = aClipRange.aEnd.Row();
2895     for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange )
2896     {
2897         const ScRange & rRange = (*pDestRanges)[nRange];
2898         SCCOL nCol1 = rRange.aStart.Col();
2899         SCROW nRow1 = rRange.aStart.Row();
2900         SCCOL nCol2 = rRange.aEnd.Col();
2901         SCROW nRow2 = rRange.aEnd.Row();
2902 
2903         if (bSkipAttrForEmpty)
2904         {
2905             // Delete cells in the destination only if their corresponding clip cells are not empty.
2906             aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
2907             DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans);
2908         }
2909         else
2910             DeleteArea(nCol1, nRow1, nCol2, nRow2, rMark, nDelFlag, false, &aBroadcastSpans);
2911 
2912         if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
2913             continue;
2914 
2915         SCCOL nC1 = nCol1;
2916         SCROW nR1 = nRow1;
2917         SCCOL nC2 = nC1 + nXw;
2918         if (nC2 > nCol2)
2919             nC2 = nCol2;
2920         SCROW nR2 = nR1 + nYw;
2921         if (nR2 > nRow2)
2922             nR2 = nRow2;
2923 
2924         const SCCOLROW nThreshold = 8192;
2925         bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold));
2926         std::vector< SCTAB > vTables;
2927 
2928         if (bPreallocatePattern)
2929         {
2930             for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i)
2931                 if (maTabs[i] && rMark.GetTableSelect( i ) )
2932                     vTables.push_back( i );
2933         }
2934 
2935         do
2936         {
2937             // Pasting is done column-wise, when pasting to a filtered
2938             // area this results in partitioning and we have to
2939             // remember and reset the start row for each column until
2940             // it can be advanced for the next chunk of unfiltered
2941             // rows.
2942             SCROW nSaveClipStartRow = nClipStartRow;
2943             do
2944             {
2945                 nClipStartRow = nSaveClipStartRow;
2946                 SCCOL nDx = nC1 - nClipStartCol;
2947                 SCROW nDy = nR1 - nClipStartRow;
2948                 if ( bIncludeFiltered )
2949                 {
2950                     CopyBlockFromClip(
2951                         aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy);
2952                     nClipStartRow += nR2 - nR1 + 1;
2953                 }
2954                 else
2955                 {
2956                     CopyNonFilteredFromClip(
2957                         aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow);
2958                 }
2959                 nC1 = nC2 + 1;
2960                 nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2);
2961             } while (nC1 <= nCol2);
2962             if (nClipStartRow > nClipEndRow)
2963                 nClipStartRow = aClipRange.aStart.Row();
2964             nC1 = nCol1;
2965             nC2 = nC1 + nXw;
2966             if (nC2 > nCol2)
2967                 nC2 = nCol2;
2968 
2969             // Preallocate pattern memory once if further chunks are to be pasted.
2970             if (bPreallocatePattern && (nR2+1) <= nRow2)
2971             {
2972                 SCROW nR3 = nR2 + 1;
2973                 for (SCTAB nTab : vTables)
2974                 {
2975                     for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2976                     {
2977                         // Pattern count of the first chunk pasted.
2978                         SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2);
2979                         // If it is only one pattern per chunk and chunks are
2980                         // pasted consecutively then it will get its range
2981                         // enlarged for each chunk and no further allocation
2982                         // happens. For non-consecutive chunks we're out of
2983                         // luck in this case.
2984                         if (nChunk > 1)
2985                         {
2986                             SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1);
2987                             SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2);
2988                             if (nNeeded > nRemain)
2989                             {
2990                                 SCSIZE nCurr = GetPatternCount( nTab, nCol);
2991                                 ReservePatternCount( nTab, nCol, nCurr + nNeeded);
2992                             }
2993                         }
2994                     }
2995                 }
2996                 bPreallocatePattern = false;
2997             }
2998 
2999             nR1 = nR2 + 1;
3000             nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2);
3001         } while (nR1 <= nRow2);
3002     }
3003 
3004     bInsertingFromOtherDoc = false;
3005 
3006     // Create Listener after everything has been inserted
3007     StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag );
3008 
3009     {
3010         ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3011 
3012         // Set all formula cells dirty, and collect non-empty non-formula cell
3013         // positions so that we can broadcast on them below.
3014         SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
3015 
3016         BroadcastAction aAction(*this);
3017         aBroadcastSpans.executeColumnAction(*this, aAction);
3018     }
3019 
3020     if (bResetCut)
3021         pClipDoc->GetClipParam().mbCutMode = false;
3022 }
3023 
3024 void ScDocument::CopyMultiRangeFromClip(
3025     const ScAddress& rDestPos, const ScMarkData& rMark, InsertDeleteFlags nInsFlag, ScDocument* pClipDoc,
3026     bool bResetCut, bool bAsLink, bool /*bIncludeFiltered*/, bool bSkipAttrForEmpty)
3027 {
3028     if (bIsClip)
3029         return;
3030 
3031     if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
3032         // There is nothing in the clip doc to copy.
3033         return;
3034 
3035     // Right now, we don't allow pasting into filtered rows, so we don't even handle it here.
3036 
3037     sc::AutoCalcSwitch aACSwitch(*this, false); // turn of auto calc temporarily.
3038     NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc);
3039 
3040     ScRange aDestRange;
3041     rMark.GetMarkArea(aDestRange);
3042 
3043     bInsertingFromOtherDoc = true;  // No Broadcast/Listener created at Insert
3044 
3045     SCCOL nCol1 = rDestPos.Col();
3046     SCROW nRow1 = rDestPos.Row();
3047     ScClipParam& rClipParam = pClipDoc->GetClipParam();
3048 
3049     sc::ColumnSpanSet aBroadcastSpans(false);
3050 
3051     if (!bSkipAttrForEmpty)
3052     {
3053         // Do the deletion first.
3054         SCCOL nColSize = rClipParam.getPasteColSize();
3055         SCROW nRowSize = rClipParam.getPasteRowSize();
3056 
3057         DeleteArea(nCol1, nRow1, nCol1+nColSize-1, nRow1+nRowSize-1, rMark, InsertDeleteFlags::CONTENTS, false, &aBroadcastSpans);
3058     }
3059 
3060     sc::CopyFromClipContext aCxt(*this, nullptr, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty);
3061     std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
3062     aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
3063 
3064     for (size_t i = 0, n = rClipParam.maRanges.size(); i < n; ++i)
3065     {
3066         const ScRange & rRange = rClipParam.maRanges[i];
3067 
3068         SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
3069         SCCOL nDx = static_cast<SCCOL>(nCol1 - rRange.aStart.Col());
3070         SCROW nDy = static_cast<SCROW>(nRow1 - rRange.aStart.Row());
3071         SCCOL nCol2 = nCol1 + rRange.aEnd.Col() - rRange.aStart.Col();
3072         SCROW nEndRow = nRow1 + nRowCount - 1;
3073 
3074         CopyBlockFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx, nDy);
3075 
3076         switch (rClipParam.meDirection)
3077         {
3078             case ScClipParam::Row:
3079                 // Begin row for the next range being pasted.
3080                 nRow1 += nRowCount;
3081             break;
3082             case ScClipParam::Column:
3083                 nCol1 += rRange.aEnd.Col() - rRange.aStart.Col() + 1;
3084             break;
3085             default:
3086                 ;
3087         }
3088     }
3089 
3090     bInsertingFromOtherDoc = false;
3091 
3092     // Create Listener after everything has been inserted
3093     StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(),
3094                            aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag );
3095 
3096     {
3097         ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3098 
3099         // Set formula cells dirty and collect non-formula cells.
3100         SetDirtyFromClip(
3101                 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(),
3102                 rMark, nInsFlag, aBroadcastSpans);
3103 
3104         BroadcastAction aAction(*this);
3105         aBroadcastSpans.executeColumnAction(*this, aAction);
3106     }
3107 
3108     if (bResetCut)
3109         pClipDoc->GetClipParam().mbCutMode = false;
3110 }
3111 
3112 void ScDocument::SetClipArea( const ScRange& rArea, bool bCut )
3113 {
3114     if (bIsClip)
3115     {
3116         ScClipParam& rClipParam = GetClipParam();
3117         rClipParam.maRanges.RemoveAll();
3118         rClipParam.maRanges.push_back(rArea);
3119         rClipParam.mbCutMode = bCut;
3120     }
3121     else
3122     {
3123         OSL_FAIL("SetClipArea: No Clip");
3124     }
3125 }
3126 
3127 void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, bool bIncludeFiltered)
3128 {
3129     if (!bIsClip)
3130     {
3131         OSL_FAIL("GetClipArea: No Clip");
3132         return;
3133     }
3134 
3135     ScRangeList& rClipRanges = GetClipParam().maRanges;
3136     if (rClipRanges.empty())
3137         // No clip range.  Bail out.
3138         return;
3139 
3140     ScRange const & rRange = rClipRanges.front();
3141     SCCOL nStartCol = rRange.aStart.Col();
3142     SCCOL nEndCol   = rRange.aEnd.Col();
3143     SCROW nStartRow = rRange.aStart.Row();
3144     SCROW nEndRow   = rRange.aEnd.Row();
3145     for ( size_t i = 1, n = rClipRanges.size(); i < n; ++i )
3146     {
3147         ScRange const & rRange2 = rClipRanges[ i ];
3148         if (rRange2.aStart.Col() < nStartCol)
3149             nStartCol = rRange2.aStart.Col();
3150         if (rRange2.aStart.Row() < nStartRow)
3151             nStartRow = rRange2.aStart.Row();
3152         if (rRange2.aEnd.Col() > nEndCol)
3153             nEndCol = rRange2.aEnd.Col();
3154         if (rRange2.aEnd.Row() < nEndRow)
3155             nEndRow = rRange2.aEnd.Row();
3156     }
3157 
3158     nClipX = nEndCol - nStartCol;
3159 
3160     if ( bIncludeFiltered )
3161         nClipY = nEndRow - nStartRow;
3162     else
3163     {
3164         //  count non-filtered rows
3165         //  count on first used table in clipboard
3166         SCTAB nCountTab = 0;
3167         while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] )
3168             ++nCountTab;
3169 
3170         SCROW nResult = CountNonFilteredRows(nStartRow, nEndRow, nCountTab);
3171 
3172         if ( nResult > 0 )
3173             nClipY = nResult - 1;
3174         else
3175             nClipY = 0;                 // always return at least 1 row
3176     }
3177 }
3178 
3179 void ScDocument::GetClipStart(SCCOL& nClipX, SCROW& nClipY)
3180 {
3181     if (bIsClip)
3182     {
3183         ScRangeList& rClipRanges = GetClipParam().maRanges;
3184         if ( !rClipRanges.empty() )
3185         {
3186             nClipX = rClipRanges.front().aStart.Col();
3187             nClipY = rClipRanges.front().aStart.Row();
3188         }
3189     }
3190     else
3191     {
3192         OSL_FAIL("GetClipStart: No Clip");
3193     }
3194 }
3195 
3196 bool ScDocument::HasClipFilteredRows()
3197 {
3198     //  count on first used table in clipboard
3199     SCTAB nCountTab = 0;
3200     while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] )
3201         ++nCountTab;
3202 
3203     ScRangeList& rClipRanges = GetClipParam().maRanges;
3204     if ( rClipRanges.empty() )
3205         return false;
3206 
3207     for ( size_t i = 0, n = rClipRanges.size(); i < n; ++i )
3208     {
3209         ScRange & rRange = rClipRanges[ i ];
3210         bool bAnswer = maTabs[nCountTab]->HasFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row());
3211         if (bAnswer)
3212             return true;
3213     }
3214     return false;
3215 }
3216 
3217 void ScDocument::MixDocument( const ScRange& rRange, ScPasteFunc nFunction, bool bSkipEmpty,
3218                                     ScDocument* pSrcDoc )
3219 {
3220     SCTAB nTab1 = rRange.aStart.Tab();
3221     SCTAB nTab2 = rRange.aEnd.Tab();
3222     sc::MixDocContext aCxt(*this);
3223     SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pSrcDoc->maTabs.size()));
3224     for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
3225     {
3226         ScTable* pTab = FetchTable(i);
3227         const ScTable* pSrcTab = pSrcDoc->FetchTable(i);
3228         if (!pTab || !pSrcTab)
3229             continue;
3230 
3231         pTab->MixData(
3232             aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
3233             nFunction, bSkipEmpty, pSrcTab);
3234     }
3235 }
3236 
3237 void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
3238                                 InsertDeleteFlags nFlags, ScPasteFunc nFunction,
3239                                 bool bSkipEmpty, bool bAsLink )
3240 {
3241     InsertDeleteFlags nDelFlags = nFlags;
3242     if (nDelFlags & InsertDeleteFlags::CONTENTS)
3243         nDelFlags |= InsertDeleteFlags::CONTENTS;          // Either all contents or delete nothing!
3244 
3245     SCTAB nSrcTab = rSrcArea.aStart.Tab();
3246 
3247     if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab])
3248     {
3249         SCCOL nStartCol = rSrcArea.aStart.Col();
3250         SCROW nStartRow = rSrcArea.aStart.Row();
3251         SCCOL nEndCol = rSrcArea.aEnd.Col();
3252         SCROW nEndRow = rSrcArea.aEnd.Row();
3253         ScDocumentUniquePtr pMixDoc;
3254         bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
3255 
3256         bool bOldAutoCalc = GetAutoCalc();
3257         SetAutoCalc( false );                   // avoid multiple calculations
3258 
3259         sc::CopyToDocContext aCxt(*this);
3260         sc::MixDocContext aMixDocCxt(*this);
3261 
3262         SCTAB nCount = static_cast<SCTAB>(maTabs.size());
3263         for (const SCTAB& i : rMark)
3264         {
3265             if (i >= nCount)
3266                 break;
3267             if (i != nSrcTab && maTabs[i])
3268             {
3269                 if (bDoMix)
3270                 {
3271                     if (!pMixDoc)
3272                     {
3273                         pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
3274                         pMixDoc->InitUndo( this, i, i );
3275                     }
3276                     else
3277                         pMixDoc->AddUndoTab( i, i );
3278 
3279                     // context used for copying content to the temporary mix document.
3280                     sc::CopyToDocContext aMixCxt(*pMixDoc);
3281                     maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3282                                            InsertDeleteFlags::CONTENTS, false, pMixDoc->maTabs[i].get(),
3283                                            /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true,
3284                                            /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3285                 }
3286                 maTabs[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags);
3287                 maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3288                                              nFlags, false, maTabs[i].get(), nullptr, bAsLink,
3289                                              /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3290 
3291                 if (bDoMix)
3292                     maTabs[i]->MixData(aMixDocCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3293                                        nFunction, bSkipEmpty, pMixDoc->maTabs[i].get() );
3294             }
3295         }
3296 
3297         SetAutoCalc( bOldAutoCalc );
3298     }
3299     else
3300     {
3301         OSL_FAIL("wrong table");
3302     }
3303 }
3304 
3305 void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
3306                                 InsertDeleteFlags nFlags, ScPasteFunc nFunction,
3307                                 bool bSkipEmpty, bool bAsLink )
3308 {
3309     InsertDeleteFlags nDelFlags = nFlags;
3310     if (nDelFlags & InsertDeleteFlags::CONTENTS)
3311         nDelFlags |= InsertDeleteFlags::CONTENTS;          // Either all contents or delete nothing!
3312 
3313     if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab])
3314     {
3315         ScDocumentUniquePtr pMixDoc;
3316         bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
3317 
3318         bool bOldAutoCalc = GetAutoCalc();
3319         SetAutoCalc( false );                   // avoid multiple calculations
3320 
3321         ScRange aArea;
3322         rMark.GetMultiMarkArea( aArea );
3323         SCCOL nStartCol = aArea.aStart.Col();
3324         SCROW nStartRow = aArea.aStart.Row();
3325         SCCOL nEndCol = aArea.aEnd.Col();
3326         SCROW nEndRow = aArea.aEnd.Row();
3327 
3328         sc::CopyToDocContext aCxt(*this);
3329         sc::MixDocContext aMixDocCxt(*this);
3330         SCTAB nCount = static_cast<SCTAB>(maTabs.size());
3331         for (const SCTAB& i : rMark)
3332         {
3333             if (i >= nCount)
3334                 break;
3335             if ( i != nSrcTab && maTabs[i] )
3336             {
3337                 if (bDoMix)
3338                 {
3339                     if (!pMixDoc)
3340                     {
3341                         pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
3342                         pMixDoc->InitUndo( this, i, i );
3343                     }
3344                     else
3345                         pMixDoc->AddUndoTab( i, i );
3346 
3347                     sc::CopyToDocContext aMixCxt(*pMixDoc);
3348                     maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3349                                            InsertDeleteFlags::CONTENTS, true, pMixDoc->maTabs[i].get(), &rMark,
3350                                            /*bAsLink*/false, /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false,
3351                                            /*bCopyCaptions*/true );
3352                 }
3353 
3354                 maTabs[i]->DeleteSelection( nDelFlags, rMark );
3355                 maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3356                                              nFlags, true, maTabs[i].get(), &rMark, bAsLink,
3357                                              /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3358 
3359                 if (bDoMix)
3360                     maTabs[i]->MixMarked(aMixDocCxt, rMark, nFunction, bSkipEmpty, pMixDoc->maTabs[i].get());
3361             }
3362         }
3363 
3364         SetAutoCalc( bOldAutoCalc );
3365     }
3366     else
3367     {
3368         OSL_FAIL("wrong table");
3369     }
3370 }
3371 
3372 bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString,
3373                             const ScSetStringParam* pParam )
3374 {
3375     ScTable* pTab = FetchTable(nTab);
3376     if (!pTab)
3377         return false;
3378 
3379     const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(nCol, nRow);
3380     if (pCurCellFormula && pCurCellFormula->IsShared())
3381     {
3382         // In case setting this string affects an existing formula group, end
3383         // its listening to purge then empty cell broadcasters. Affected
3384         // remaining split group listeners will be set up again via
3385         // ScColumn::DetachFormulaCell() and
3386         // ScColumn::StartListeningUnshared().
3387 
3388         sc::EndListeningContext aCxt(*this);
3389         ScAddress aPos(nCol, nRow, nTab);
3390         EndListeningIntersectedGroup(aCxt, aPos, nullptr);
3391         aCxt.purgeEmptyBroadcasters();
3392     }
3393 
3394     return pTab->SetString(nCol, nRow, nTab, rString, pParam);
3395 }
3396 
3397 bool ScDocument::SetString(
3398     const ScAddress& rPos, const OUString& rString, const ScSetStringParam* pParam )
3399 {
3400     return SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rString, pParam);
3401 }
3402 
3403 bool ScDocument::SetEditText( const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText )
3404 {
3405     if (!TableExists(rPos.Tab()))
3406     {
3407         return false;
3408     }
3409 
3410     return maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), std::move(pEditText));
3411 }
3412 
3413 void ScDocument::SetEditText( const ScAddress& rPos, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
3414 {
3415     if (!TableExists(rPos.Tab()))
3416         return;
3417 
3418     maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEditText, pEditPool);
3419 }
3420 
3421 void ScDocument::SetEditText( const ScAddress& rPos, const OUString& rStr )
3422 {
3423     if (!TableExists(rPos.Tab()))
3424         return;
3425 
3426     ScFieldEditEngine& rEngine = GetEditEngine();
3427     rEngine.SetText(rStr);
3428     maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
3429 }
3430 
3431 SCROW ScDocument::GetFirstEditTextRow( const ScRange& rRange ) const
3432 {
3433     const ScTable* pTab = FetchTable(rRange.aStart.Tab());
3434     if (!pTab)
3435         return -1;
3436 
3437     return pTab->GetFirstEditTextRow(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
3438 }
3439 
3440 void ScDocument::SetTextCell( const ScAddress& rPos, const OUString& rStr )
3441 {
3442     if (!TableExists(rPos.Tab()))
3443         return;
3444 
3445     if (ScStringUtil::isMultiline(rStr))
3446     {
3447         ScFieldEditEngine& rEngine = GetEditEngine();
3448         rEngine.SetText(rStr);
3449         maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
3450     }
3451     else
3452     {
3453         ScSetStringParam aParam;
3454         aParam.setTextInput();
3455         maTabs[rPos.Tab()]->SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rStr, &aParam);
3456     }
3457 }
3458 
3459 void ScDocument::SetEmptyCell( const ScAddress& rPos )
3460 {
3461     if (!TableExists(rPos.Tab()))
3462         return;
3463 
3464     maTabs[rPos.Tab()]->SetEmptyCell(rPos.Col(), rPos.Row());
3465 }
3466 
3467 void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal )
3468 {
3469     SetValue(ScAddress(nCol, nRow, nTab), rVal);
3470 }
3471 
3472 void ScDocument::SetValue( const ScAddress& rPos, double fVal )
3473 {
3474     ScTable* pTab = FetchTable(rPos.Tab());
3475     if (!pTab)
3476         return;
3477 
3478     const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(rPos.Col(), rPos.Row());
3479     if (pCurCellFormula && pCurCellFormula->IsShared())
3480     {
3481         // In case setting this value affects an existing formula group, end
3482         // its listening to purge then empty cell broadcasters. Affected
3483         // remaining split group listeners will be set up again via
3484         // ScColumn::DetachFormulaCell() and
3485         // ScColumn::StartListeningUnshared().
3486 
3487         sc::EndListeningContext aCxt(*this);
3488         EndListeningIntersectedGroup(aCxt, rPos, nullptr);
3489         aCxt.purgeEmptyBroadcasters();
3490     }
3491 
3492     pTab->SetValue(rPos.Col(), rPos.Row(), fVal);
3493 }
3494 
3495 OUString ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext* pContext ) const
3496 {
3497     if (TableExists(nTab))
3498     {
3499         OUString aStr;
3500         maTabs[nTab]->GetString(nCol, nRow, aStr, pContext);
3501         return aStr;
3502     }
3503     else
3504         return EMPTY_OUSTRING;
3505 }
3506 
3507 OUString ScDocument::GetString( const ScAddress& rPos, const ScInterpreterContext* pContext ) const
3508 {
3509     if (!TableExists(rPos.Tab()))
3510         return EMPTY_OUSTRING;
3511 
3512     OUString aStr;
3513     maTabs[rPos.Tab()]->GetString(rPos.Col(), rPos.Row(), aStr, pContext);
3514     return aStr;
3515 }
3516 
3517 double* ScDocument::GetValueCell( const ScAddress& rPos )
3518 {
3519     if (!TableExists(rPos.Tab()))
3520         return nullptr;
3521 
3522     return maTabs[rPos.Tab()]->GetValueCell(rPos.Col(), rPos.Row());
3523 }
3524 
3525 svl::SharedString ScDocument::GetSharedString( const ScAddress& rPos ) const
3526 {
3527     if (!TableExists(rPos.Tab()))
3528         return svl::SharedString();
3529 
3530     return maTabs[rPos.Tab()]->GetSharedString(rPos.Col(), rPos.Row());
3531 }
3532 
3533 std::shared_ptr<sc::FormulaGroupContext>& ScDocument::GetFormulaGroupContext()
3534 {
3535     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
3536     if (!mpFormulaGroupCxt)
3537         mpFormulaGroupCxt.reset(new sc::FormulaGroupContext);
3538 
3539     return mpFormulaGroupCxt;
3540 }
3541 
3542 void ScDocument::DiscardFormulaGroupContext()
3543 {
3544     assert(!IsThreadedGroupCalcInProgress());
3545     if( !mbFormulaGroupCxtBlockDiscard )
3546         mpFormulaGroupCxt.reset();
3547 }
3548 
3549 void ScDocument::GetInputString( SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rString )
3550 {
3551     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3552         maTabs[nTab]->GetInputString( nCol, nRow, rString );
3553     else
3554         rString.clear();
3555 }
3556 
3557 FormulaError ScDocument::GetStringForFormula( const ScAddress& rPos, OUString& rString )
3558 {
3559     // Used in formulas (add-in parameters etc), so it must use the same semantics as
3560     // ScInterpreter::GetCellString: always format values as numbers.
3561     // The return value is the error code.
3562 
3563     ScRefCellValue aCell(*this, rPos);
3564     if (aCell.isEmpty())
3565     {
3566         rString = EMPTY_OUSTRING;
3567         return FormulaError::NONE;
3568     }
3569 
3570     FormulaError nErr = FormulaError::NONE;
3571     OUString aStr;
3572     SvNumberFormatter* pFormatter = GetFormatTable();
3573     switch (aCell.meType)
3574     {
3575         case CELLTYPE_STRING:
3576         case CELLTYPE_EDIT:
3577             aStr = aCell.getString(this);
3578         break;
3579         case CELLTYPE_FORMULA:
3580         {
3581             ScFormulaCell* pFCell = aCell.mpFormula;
3582             nErr = pFCell->GetErrCode();
3583             if (pFCell->IsValue())
3584             {
3585                 double fVal = pFCell->GetValue();
3586                 sal_uInt32 nIndex = pFormatter->GetStandardFormat(
3587                                     SvNumFormatType::NUMBER,
3588                                     ScGlobal::eLnge);
3589                 pFormatter->GetInputLineString(fVal, nIndex, aStr);
3590             }
3591             else
3592                 aStr = pFCell->GetString().getString();
3593         }
3594         break;
3595         case CELLTYPE_VALUE:
3596         {
3597             double fVal = aCell.mfValue;
3598             sal_uInt32 nIndex = pFormatter->GetStandardFormat(
3599                                     SvNumFormatType::NUMBER,
3600                                     ScGlobal::eLnge);
3601             pFormatter->GetInputLineString(fVal, nIndex, aStr);
3602         }
3603         break;
3604         default:
3605             ;
3606     }
3607 
3608     rString = aStr;
3609     return nErr;
3610 }
3611 
3612 void ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, double& rValue ) const
3613 {
3614     if (TableExists(nTab))
3615         rValue = maTabs[nTab]->GetValue( nCol, nRow );
3616     else
3617         rValue = 0.0;
3618 }
3619 
3620 const EditTextObject* ScDocument::GetEditText( const ScAddress& rPos ) const
3621 {
3622     SCTAB nTab = rPos.Tab();
3623     if (!TableExists(nTab))
3624         return nullptr;
3625 
3626     return maTabs[nTab]->GetEditText(rPos.Col(), rPos.Row());
3627 }
3628 
3629 void ScDocument::RemoveEditTextCharAttribs( const ScAddress& rPos, const ScPatternAttr& rAttr )
3630 {
3631     if (!TableExists(rPos.Tab()))
3632         return;
3633 
3634     return maTabs[rPos.Tab()]->RemoveEditTextCharAttribs(rPos.Col(), rPos.Row(), rAttr);
3635 }
3636 
3637 double ScDocument::GetValue( const ScAddress& rPos ) const
3638 {
3639     SCTAB nTab = rPos.Tab();
3640     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3641         return maTabs[nTab]->GetValue(rPos.Col(), rPos.Row());
3642     return 0.0;
3643 }
3644 
3645 double ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3646 {
3647     ScAddress aAdr(nCol, nRow, nTab); return GetValue(aAdr);
3648 }
3649 
3650 void ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab,
3651                                   sal_uInt32& rFormat ) const
3652 {
3653     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
3654         if (maTabs[nTab])
3655         {
3656             rFormat = maTabs[nTab]->GetNumberFormat( nCol, nRow );
3657             return ;
3658         }
3659     rFormat = 0;
3660 }
3661 
3662 sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const
3663 {
3664     SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
3665     SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
3666     SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
3667 
3668     if (!TableExists(nTab1) || !TableExists(nTab2))
3669         return 0;
3670 
3671     sal_uInt32 nFormat = 0;
3672     bool bFirstItem = true;
3673     for (SCTAB nTab = nTab1; nTab <= nTab2 && nTab < static_cast<SCTAB>(maTabs.size()) ; ++nTab)
3674         for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
3675         {
3676             sal_uInt32 nThisFormat = maTabs[nTab]->GetNumberFormat(nCol, nRow1, nRow2);
3677             if (bFirstItem)
3678             {
3679                 nFormat = nThisFormat;
3680                 bFirstItem = false;
3681             }
3682             else if (nThisFormat != nFormat)
3683                 return 0;
3684         }
3685 
3686     return nFormat;
3687 }
3688 
3689 sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
3690 {
3691     SCTAB nTab = rPos.Tab();
3692     if (!TableExists(nTab))
3693         return 0;
3694 
3695     return maTabs[nTab]->GetNumberFormat( rContext, rPos );
3696 }
3697 
3698 void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat )
3699 {
3700     assert(!IsThreadedGroupCalcInProgress());
3701     SCTAB nTab = rPos.Tab();
3702     if (!TableExists(nTab))
3703         return;
3704 
3705     maTabs[nTab]->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat);
3706 }
3707 
3708 void ScDocument::GetNumberFormatInfo( const ScInterpreterContext& rContext, SvNumFormatType& nType, sal_uInt32& nIndex,
3709             const ScAddress& rPos ) const
3710 {
3711     SCTAB nTab = rPos.Tab();
3712     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3713     {
3714         nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos );
3715         nType = rContext.GetFormatTable()->GetType( nIndex );
3716     }
3717     else
3718     {
3719         nType = SvNumFormatType::UNDEFINED;
3720         nIndex = 0;
3721     }
3722 }
3723 
3724 void ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rFormula ) const
3725 {
3726     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3727             maTabs[nTab]->GetFormula( nCol, nRow, rFormula );
3728     else
3729         rFormula.clear();
3730 }
3731 
3732 const ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos ) const
3733 {
3734     if (!TableExists(rPos.Tab()))
3735         return nullptr;
3736 
3737     return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row());
3738 }
3739 
3740 ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos )
3741 {
3742     if (!TableExists(rPos.Tab()))
3743         return nullptr;
3744 
3745     return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row());
3746 }
3747 
3748 CellType ScDocument::GetCellType( const ScAddress& rPos ) const
3749 {
3750     SCTAB nTab = rPos.Tab();
3751     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3752         return maTabs[nTab]->GetCellType( rPos );
3753     return CELLTYPE_NONE;
3754 }
3755 
3756 void ScDocument::GetCellType( SCCOL nCol, SCROW nRow, SCTAB nTab,
3757         CellType& rCellType ) const
3758 {
3759     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
3760         rCellType = maTabs[nTab]->GetCellType( nCol, nRow );
3761     else
3762         rCellType = CELLTYPE_NONE;
3763 }
3764 
3765 bool ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3766 {
3767     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3768             return maTabs[nTab]->HasStringData( nCol, nRow );
3769     else
3770         return false;
3771 }
3772 
3773 bool ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3774 {
3775     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
3776             return maTabs[nTab]->HasValueData( nCol, nRow );
3777     else
3778         return false;
3779 }
3780 
3781 bool ScDocument::HasValueData( const ScAddress& rPos ) const
3782 {
3783     return HasValueData(rPos.Col(), rPos.Row(), rPos.Tab());
3784 }
3785 
3786 bool ScDocument::HasStringCells( const ScRange& rRange ) const
3787 {
3788     //  true, if String- or Edit cells in range
3789 
3790     SCCOL nStartCol = rRange.aStart.Col();
3791     SCROW nStartRow = rRange.aStart.Row();
3792     SCTAB nStartTab = rRange.aStart.Tab();
3793     SCCOL nEndCol = rRange.aEnd.Col();
3794     SCROW nEndRow = rRange.aEnd.Row();
3795     SCTAB nEndTab = rRange.aEnd.Tab();
3796 
3797     for ( SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
3798         if ( maTabs[nTab] && maTabs[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) )
3799             return true;
3800 
3801     return false;
3802 }
3803 
3804 bool ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3805 {
3806     sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
3807     if( nValidation )
3808     {
3809         const ScValidationData* pData = GetValidationEntry( nValidation );
3810         if( pData && pData->HasSelectionList() )
3811             return true;
3812     }
3813     return HasStringCells( ScRange( nCol, 0, nTab, nCol, MAXROW, nTab ) );
3814 }
3815 
3816 bool ScDocument::HasValidationData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3817 {
3818     sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
3819     if( nValidation )
3820     {
3821         const ScValidationData* pData = GetValidationEntry( nValidation );
3822         if( pData && pData->GetDataMode() != ScValidationMode::SC_VALID_ANY )
3823             return true;
3824     }
3825     return false;
3826 }
3827 
3828 void ScDocument::CheckVectorizationState()
3829 {
3830     bool bOldAutoCalc = GetAutoCalc();
3831     bAutoCalc = false;      // no multiple calculations
3832 
3833     for (const auto& a : maTabs)
3834     {
3835         if (a)
3836             a->CheckVectorizationState();
3837     }
3838 
3839     SetAutoCalc(bOldAutoCalc);
3840 }
3841 
3842 void ScDocument::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
3843 {
3844     bool bOldAutoCalc = GetAutoCalc();
3845     bAutoCalc = false;      // no multiple calculations
3846     {   // scope for bulk broadcast
3847         ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3848         for (const auto& a : maTabs)
3849         {
3850             if (a)
3851                 a->SetAllFormulasDirty(rCxt);
3852         }
3853     }
3854 
3855     // Although Charts are also set to dirty in Tracking without AutoCalc
3856     // if all formulas are dirty, the charts can no longer be caught
3857     //  (#45205#) - that is why all Charts have to be explicitly handled again
3858     if (pChartListenerCollection)
3859         pChartListenerCollection->SetDirty();
3860 
3861     SetAutoCalc( bOldAutoCalc );
3862 }
3863 
3864 void ScDocument::SetDirty( const ScRange& rRange, bool bIncludeEmptyCells )
3865 {
3866     bool bOldAutoCalc = GetAutoCalc();
3867     bAutoCalc = false;      // no multiple calculations
3868     {   // scope for bulk broadcast
3869         ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3870         SCTAB nTab2 = rRange.aEnd.Tab();
3871         for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
3872             if (maTabs[i]) maTabs[i]->SetDirty( rRange,
3873                     (bIncludeEmptyCells ? ScColumn::BROADCAST_BROADCASTERS : ScColumn::BROADCAST_DATA_POSITIONS));
3874 
3875         /* TODO: this now also notifies conditional formatting and does an UNO
3876          * broadcast, which wasn't done here before. Is that an actually
3877          * desired side effect, or should we come up with a method that
3878          * doesn't? */
3879         if (bIncludeEmptyCells)
3880             BroadcastCells( rRange, SfxHintId::ScDataChanged, false);
3881     }
3882     SetAutoCalc( bOldAutoCalc );
3883 }
3884 
3885 void ScDocument::SetTableOpDirty( const ScRange& rRange )
3886 {
3887     bool bOldAutoCalc = GetAutoCalc();
3888     bAutoCalc = false;      // no multiple recalculation
3889     SCTAB nTab2 = rRange.aEnd.Tab();
3890     for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
3891         if (maTabs[i]) maTabs[i]->SetTableOpDirty( rRange );
3892     SetAutoCalc( bOldAutoCalc );
3893 }
3894 
3895 void ScDocument::InterpretDirtyCells( const ScRangeList& rRanges )
3896 {
3897     if (!GetAutoCalc())
3898         return;
3899 
3900     PrepareFormulaCalc();
3901 
3902     for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
3903     {
3904         const ScRange& rRange = rRanges[nPos];
3905         for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
3906         {
3907             ScTable* pTab = FetchTable(nTab);
3908             if (!pTab)
3909                 return;
3910 
3911             pTab->InterpretDirtyCells(
3912                 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
3913         }
3914     }
3915 
3916     ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
3917     mpFormulaGroupCxt.reset();
3918 }
3919 
3920 void ScDocument::AddTableOpFormulaCell( ScFormulaCell* pCell )
3921 {
3922     if (!m_TableOpList.empty())
3923     {
3924         ScInterpreterTableOpParams *const p = m_TableOpList.back();
3925         if ( p->bCollectNotifications )
3926         {
3927             if ( p->bRefresh )
3928             {   // refresh pointers only
3929                 p->aNotifiedFormulaCells.push_back( pCell );
3930             }
3931             else
3932             {   // init both, address and pointer
3933                 p->aNotifiedFormulaCells.push_back( pCell );
3934                 p->aNotifiedFormulaPos.push_back( pCell->aPos );
3935             }
3936         }
3937     }
3938 }
3939 
3940 void ScDocument::CalcAll()
3941 {
3942     PrepareFormulaCalc();
3943     ClearLookupCaches();    // Ensure we don't deliver zombie data.
3944     sc::AutoCalcSwitch aSwitch(*this, true);
3945     for (const auto& a : maTabs)
3946     {
3947         if (a)
3948             a->SetDirtyVar();
3949     }
3950     for (const auto& a : maTabs)
3951     {
3952         if (a)
3953             a->CalcAll();
3954     }
3955     ClearFormulaTree();
3956 
3957     // In eternal hard recalc state caches were not added as listeners,
3958     // invalidate them so the next non-CalcAll() normal lookup will not be
3959     // presented with outdated data.
3960     if (GetHardRecalcState() == HardRecalcState::ETERNAL)
3961         ClearLookupCaches();
3962 }
3963 
3964 void ScDocument::CompileAll()
3965 {
3966     sc::CompileFormulaContext aCxt(this);
3967     for (const auto& a : maTabs)
3968     {
3969         if (a)
3970             a->CompileAll(aCxt);
3971     }
3972 
3973     sc::SetFormulaDirtyContext aFormulaDirtyCxt;
3974     SetAllFormulasDirty(aFormulaDirtyCxt);
3975 }
3976 
3977 void ScDocument::CompileXML()
3978 {
3979     bool bOldAutoCalc = GetAutoCalc();
3980     SetAutoCalc( false );
3981     ScProgress aProgress( GetDocumentShell(), ScResId(
3982                 STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount(), true );
3983 
3984     sc::CompileFormulaContext aCxt(this);
3985 
3986     // set AutoNameCache to speed up automatic name lookup
3987     OSL_ENSURE( !pAutoNameCache, "AutoNameCache already set" );
3988     pAutoNameCache.reset( new ScAutoNameCache( this ) );
3989 
3990     if (pRangeName)
3991         pRangeName->CompileUnresolvedXML(aCxt);
3992 
3993     std::for_each(maTabs.begin(), maTabs.end(),
3994         [&](ScTableUniquePtr & pTab)
3995         {
3996             if (pTab)
3997                 pTab->CompileXML(aCxt, aProgress);
3998         }
3999     );
4000     StartAllListeners();
4001 
4002     pAutoNameCache.reset();  // valid only during CompileXML, where cell contents don't change
4003 
4004     if ( pValidationList )
4005     {
4006         ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
4007         pValidationList->CompileXML();
4008     }
4009 
4010     // Track all formula cells that were appended to the FormulaTrack during
4011     // import or CompileXML().
4012     TrackFormulas();
4013 
4014     SetAutoCalc( bOldAutoCalc );
4015 }
4016 
4017 bool ScDocument::CompileErrorCells(FormulaError nErrCode)
4018 {
4019     bool bCompiled = false;
4020     sc::CompileFormulaContext aCxt(this);
4021     for (const auto& a : maTabs)
4022     {
4023         if (!a)
4024             continue;
4025 
4026         if (a->CompileErrorCells(aCxt, nErrCode))
4027             bCompiled = true;
4028     }
4029 
4030     return bCompiled;
4031 }
4032 
4033 void ScDocument::CalcAfterLoad( bool bStartListening )
4034 {
4035     if (bIsClip)    // Excel data is loaded from the Clipboard to a Clip-Doc
4036         return;     // the calculation is then only performed when inserting into the real document
4037 
4038     bCalcingAfterLoad = true;
4039     sc::CompileFormulaContext aCxt(this);
4040     {
4041         for (const auto& a : maTabs)
4042         {
4043             if (a)
4044                 a->CalcAfterLoad(aCxt, bStartListening);
4045         }
4046         for (const auto& a : maTabs)
4047         {
4048             if (a)
4049                 a->SetDirtyAfterLoad();
4050         }
4051     }
4052     bCalcingAfterLoad = false;
4053 
4054     SetDetectiveDirty(false);   // No real changes yet
4055 
4056     // #i112436# If formula cells are already dirty, they don't broadcast further changes.
4057     // So the source ranges of charts must be interpreted even if they are not visible,
4058     // similar to ScMyShapeResizer::CreateChartListener for loading own files (i104899).
4059     if (pChartListenerCollection)
4060     {
4061         const ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners();
4062         for (auto const& it : rListeners)
4063         {
4064             const ScChartListener *const p = it.second.get();
4065             InterpretDirtyCells(*p->GetRangeList());
4066         }
4067     }
4068 }
4069 
4070 FormulaError ScDocument::GetErrCode( const ScAddress& rPos ) const
4071 {
4072     SCTAB nTab = rPos.Tab();
4073     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4074         return maTabs[nTab]->GetErrCode( rPos );
4075     return FormulaError::NONE;
4076 }
4077 
4078 void ScDocument::ResetChanged( const ScRange& rRange )
4079 {
4080     SCTAB nTabSize = static_cast<SCTAB>(maTabs.size());
4081     SCTAB nTab1 = rRange.aStart.Tab();
4082     SCTAB nTab2 = rRange.aEnd.Tab();
4083     for (SCTAB nTab = nTab1; nTab1 <= nTab2 && nTab < nTabSize; ++nTab)
4084         if (maTabs[nTab])
4085             maTabs[nTab]->ResetChanged(rRange);
4086 }
4087 
4088 // Column widths / Row heights   --------------------------------------
4089 
4090 void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
4091 {
4092     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4093         maTabs[nTab]->SetColWidth( nCol, nNewWidth );
4094 }
4095 
4096 void ScDocument::SetColWidthOnly( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
4097 {
4098     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4099         maTabs[nTab]->SetColWidthOnly( nCol, nNewWidth );
4100 }
4101 
4102 void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, sal_uInt16 nNewHeight )
4103 {
4104     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4105         maTabs[nTab]->SetRowHeight( nRow, nNewHeight );
4106 }
4107 
4108 void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
4109 {
4110     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4111         maTabs[nTab]->SetRowHeightRange
4112             ( nStartRow, nEndRow, nNewHeight, 1.0 );
4113 }
4114 
4115 void ScDocument::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
4116 {
4117     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4118         maTabs[nTab]->SetRowHeightOnly( nStartRow, nEndRow, nNewHeight );
4119 }
4120 
4121 void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bManual )
4122 {
4123     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4124         maTabs[nTab]->SetManualHeight( nStartRow, nEndRow, bManual );
4125 }
4126 
4127 sal_uInt16 ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
4128 {
4129     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4130         return maTabs[nTab]->GetColWidth( nCol, bHiddenAsZero );
4131     OSL_FAIL("wrong table number");
4132     return 0;
4133 }
4134 
4135 sal_uLong ScDocument::GetColWidth( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab ) const
4136 {
4137     const ScTable* pTab = FetchTable(nTab);
4138     if (!pTab)
4139         return 0;
4140 
4141     return pTab->GetColWidth(nStartCol, nEndCol);
4142 }
4143 
4144 sal_uInt16 ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const
4145 {
4146     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4147         return maTabs[nTab]->GetOriginalWidth( nCol );
4148     OSL_FAIL("wrong table number");
4149     return 0;
4150 }
4151 
4152 sal_uInt16 ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const
4153 {
4154     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4155         return maTabs[nTab]->GetCommonWidth( nEndCol );
4156     OSL_FAIL("Wrong table number");
4157     return 0;
4158 }
4159 
4160 sal_uInt16 ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const
4161 {
4162     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4163         return maTabs[nTab]->GetOriginalHeight( nRow );
4164     OSL_FAIL("Wrong table number");
4165     return 0;
4166 }
4167 
4168 sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
4169 {
4170     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4171         return maTabs[nTab]->GetRowHeight( nRow, nullptr, nullptr, bHiddenAsZero );
4172     OSL_FAIL("Wrong sheet number");
4173     return 0;
4174 }
4175 
4176 sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, SCROW* pStartRow, SCROW* pEndRow ) const
4177 {
4178     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4179         return maTabs[nTab]->GetRowHeight( nRow, pStartRow, pEndRow );
4180     OSL_FAIL("Wrong sheet number");
4181     return 0;
4182 }
4183 
4184 sal_uLong ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
4185 {
4186     if (nStartRow == nEndRow)
4187         return GetRowHeight( nStartRow, nTab, bHiddenAsZero );  // faster for a single row
4188 
4189     // check bounds because this method replaces former for(i=start;i<=end;++i) loops
4190     if (nStartRow > nEndRow)
4191         return 0;
4192 
4193     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4194         return maTabs[nTab]->GetRowHeight( nStartRow, nEndRow, bHiddenAsZero );
4195 
4196     OSL_FAIL("wrong sheet number");
4197     return 0;
4198 }
4199 
4200 SCROW ScDocument::GetRowForHeight( SCTAB nTab, sal_uLong nHeight ) const
4201 {
4202     return maTabs[nTab]->GetRowForHeight(nHeight);
4203 }
4204 
4205 sal_uLong ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow,
4206         SCTAB nTab, double fScale ) const
4207 {
4208     // faster for a single row
4209     if (nStartRow == nEndRow)
4210         return static_cast<sal_uLong>(GetRowHeight( nStartRow, nTab) * fScale);
4211 
4212     // check bounds because this method replaces former for(i=start;i<=end;++i) loops
4213     if (nStartRow > nEndRow)
4214         return 0;
4215 
4216     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4217         return maTabs[nTab]->GetScaledRowHeight( nStartRow, nEndRow, fScale);
4218 
4219     OSL_FAIL("wrong sheet number");
4220     return 0;
4221 }
4222 
4223 SCROW ScDocument::GetHiddenRowCount( SCROW nRow, SCTAB nTab ) const
4224 {
4225     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4226         return maTabs[nTab]->GetHiddenRowCount( nRow );
4227     OSL_FAIL("wrong table number");
4228     return 0;
4229 }
4230 
4231 sal_uLong ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
4232 {
4233     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4234         return maTabs[nTab]->GetColOffset( nCol, bHiddenAsZero );
4235     OSL_FAIL("wrong table number");
4236     return 0;
4237 }
4238 
4239 sal_uLong ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
4240 {
4241     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4242         return maTabs[nTab]->GetRowOffset( nRow, bHiddenAsZero );
4243     OSL_FAIL("wrong table number");
4244     return 0;
4245 }
4246 
4247 sal_uInt16 ScDocument::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, OutputDevice* pDev,
4248                                        double nPPTX, double nPPTY,
4249                                        const Fraction& rZoomX, const Fraction& rZoomY,
4250                                        bool bFormula, const ScMarkData* pMarkData,
4251                                        const ScColWidthParam* pParam )
4252 {
4253     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4254         return maTabs[nTab]->GetOptimalColWidth( nCol, pDev, nPPTX, nPPTY,
4255             rZoomX, rZoomY, bFormula, pMarkData, pParam );
4256     OSL_FAIL("wrong table number");
4257     return 0;
4258 }
4259 
4260 long ScDocument::GetNeededSize( SCCOL nCol, SCROW nRow, SCTAB nTab,
4261                                     OutputDevice* pDev,
4262                                     double nPPTX, double nPPTY,
4263                                     const Fraction& rZoomX, const Fraction& rZoomY,
4264                                     bool bWidth, bool bTotalSize )
4265 {
4266     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4267         return maTabs[nTab]->GetNeededSize
4268                 ( nCol, nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, bTotalSize );
4269     OSL_FAIL("wrong table number");
4270     return 0;
4271 }
4272 
4273 bool ScDocument::SetOptimalHeight( sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, SCTAB nTab )
4274 {
4275     ScTable* pTab = FetchTable(nTab);
4276     if (!pTab)
4277         return false;
4278 
4279     return pTab->SetOptimalHeight(rCxt, nStartRow, nEndRow);
4280 }
4281 
4282 void ScDocument::UpdateAllRowHeights( sc::RowHeightContext& rCxt, const ScMarkData* pTabMark )
4283 {
4284     // one progress across all (selected) sheets
4285 
4286     sal_uLong nCellCount = 0;
4287     for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ )
4288         if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
4289             nCellCount += maTabs[nTab]->GetWeightedCount();
4290 
4291     ScProgress aProgress( GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true );
4292 
4293     sal_uLong nProgressStart = 0;
4294     for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ )
4295         if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
4296         {
4297             maTabs[nTab]->SetOptimalHeightOnly(rCxt, 0, MAXROW, &aProgress, nProgressStart);
4298             maTabs[nTab]->SetDrawPageSize();
4299             nProgressStart += maTabs[nTab]->GetWeightedCount();
4300         }
4301 }
4302 
4303 // Column/Row - Flags   ----------------------------------------------
4304 
4305 void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, bool bShow)
4306 {
4307     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4308         maTabs[nTab]->ShowCol( nCol, bShow );
4309 }
4310 
4311 void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, bool bShow)
4312 {
4313     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4314         maTabs[nTab]->ShowRow( nRow, bShow );
4315 }
4316 
4317 void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, bool bShow)
4318 {
4319     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4320         maTabs[nTab]->ShowRows( nRow1, nRow2, bShow );
4321 }
4322 
4323 void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, CRFlags nNewFlags )
4324 {
4325     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4326         maTabs[nTab]->SetRowFlags( nRow, nNewFlags );
4327 }
4328 
4329 void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, CRFlags nNewFlags )
4330 {
4331     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4332         maTabs[nTab]->SetRowFlags( nStartRow, nEndRow, nNewFlags );
4333 }
4334 
4335 CRFlags ScDocument::GetColFlags( SCCOL nCol, SCTAB nTab ) const
4336 {
4337     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4338         return maTabs[nTab]->GetColFlags( nCol );
4339     OSL_FAIL("wrong table number");
4340     return CRFlags::NONE;
4341 }
4342 
4343 CRFlags ScDocument::GetRowFlags( SCROW nRow, SCTAB nTab ) const
4344 {
4345     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4346         return maTabs[nTab]->GetRowFlags( nRow );
4347     OSL_FAIL("wrong table number");
4348     return CRFlags::NONE;
4349 }
4350 
4351 void ScDocument::GetAllRowBreaks(set<SCROW>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
4352 {
4353     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4354         return;
4355     maTabs[nTab]->GetAllRowBreaks(rBreaks, bPage, bManual);
4356 }
4357 
4358 void ScDocument::GetAllColBreaks(set<SCCOL>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
4359 {
4360     if (!ValidTab(nTab) || !maTabs[nTab])
4361         return;
4362 
4363     maTabs[nTab]->GetAllColBreaks(rBreaks, bPage, bManual);
4364 }
4365 
4366 ScBreakType ScDocument::HasRowBreak(SCROW nRow, SCTAB nTab) const
4367 {
4368     ScBreakType nType = ScBreakType::NONE;
4369     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
4370         return nType;
4371 
4372     if (maTabs[nTab]->HasRowPageBreak(nRow))
4373         nType |= ScBreakType::Page;
4374 
4375     if (maTabs[nTab]->HasRowManualBreak(nRow))
4376         nType |= ScBreakType::Manual;
4377 
4378     return nType;
4379 }
4380 
4381 ScBreakType ScDocument::HasColBreak(SCCOL nCol, SCTAB nTab) const
4382 {
4383     ScBreakType nType = ScBreakType::NONE;
4384     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
4385         return nType;
4386 
4387     if (maTabs[nTab]->HasColPageBreak(nCol))
4388         nType |= ScBreakType::Page;
4389 
4390     if (maTabs[nTab]->HasColManualBreak(nCol))
4391         nType |= ScBreakType::Manual;
4392 
4393     return nType;
4394 }
4395 
4396 void ScDocument::SetRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
4397 {
4398     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
4399         return;
4400 
4401     maTabs[nTab]->SetRowBreak(nRow, bPage, bManual);
4402 }
4403 
4404 void ScDocument::SetColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
4405 {
4406     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
4407         return;
4408 
4409     maTabs[nTab]->SetColBreak(nCol, bPage, bManual);
4410 }
4411 
4412 void ScDocument::RemoveRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
4413 {
4414     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
4415         return;
4416 
4417     maTabs[nTab]->RemoveRowBreak(nRow, bPage, bManual);
4418 }
4419 
4420 void ScDocument::RemoveColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
4421 {
4422     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
4423         return;
4424 
4425     maTabs[nTab]->RemoveColBreak(nCol, bPage, bManual);
4426 }
4427 
4428 Sequence<TablePageBreakData> ScDocument::GetRowBreakData(SCTAB nTab) const
4429 {
4430     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4431         return Sequence<TablePageBreakData>();
4432 
4433     return maTabs[nTab]->GetRowBreakData();
4434 }
4435 
4436 bool ScDocument::RowHidden(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
4437 {
4438     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4439         return false;
4440 
4441     return maTabs[nTab]->RowHidden(nRow, pFirstRow, pLastRow);
4442 }
4443 
4444 bool ScDocument::HasHiddenRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4445 {
4446     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4447         return false;
4448 
4449     return maTabs[nTab]->HasHiddenRows(nStartRow, nEndRow);
4450 }
4451 
4452 bool ScDocument::ColHidden(SCCOL nCol, SCTAB nTab, SCCOL* pFirstCol, SCCOL* pLastCol) const
4453 {
4454     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4455     {
4456         if (pFirstCol)
4457             *pFirstCol = nCol;
4458         if (pLastCol)
4459             *pLastCol = nCol;
4460         return false;
4461     }
4462 
4463     return maTabs[nTab]->ColHidden(nCol, pFirstCol, pLastCol);
4464 }
4465 
4466 void ScDocument::SetRowHidden(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHidden)
4467 {
4468     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4469         return;
4470 
4471     maTabs[nTab]->SetRowHidden(nStartRow, nEndRow, bHidden);
4472 }
4473 
4474 void ScDocument::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bHidden)
4475 {
4476     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4477         return;
4478 
4479     maTabs[nTab]->SetColHidden(nStartCol, nEndCol, bHidden);
4480 }
4481 
4482 SCROW ScDocument::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4483 {
4484     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4485         return ::std::numeric_limits<SCROW>::max();
4486 
4487     return maTabs[nTab]->FirstVisibleRow(nStartRow, nEndRow);
4488 }
4489 
4490 SCROW ScDocument::LastVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4491 {
4492     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4493         return ::std::numeric_limits<SCROW>::max();
4494 
4495     return maTabs[nTab]->LastVisibleRow(nStartRow, nEndRow);
4496 }
4497 
4498 SCROW ScDocument::CountVisibleRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4499 {
4500     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4501         return 0;
4502 
4503     return maTabs[nTab]->CountVisibleRows(nStartRow, nEndRow);
4504 }
4505 
4506 bool ScDocument::RowFiltered(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
4507 {
4508     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4509         return false;
4510 
4511     return maTabs[nTab]->RowFiltered(nRow, pFirstRow, pLastRow);
4512 }
4513 
4514 bool ScDocument::HasFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4515 {
4516     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4517         return false;
4518 
4519     return maTabs[nTab]->HasFilteredRows(nStartRow, nEndRow);
4520 }
4521 
4522 bool ScDocument::ColFiltered(SCCOL nCol, SCTAB nTab) const
4523 {
4524     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4525         return false;
4526 
4527     return maTabs[nTab]->ColFiltered(nCol);
4528 }
4529 
4530 void ScDocument::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bFiltered)
4531 {
4532     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4533         return;
4534 
4535     maTabs[nTab]->SetRowFiltered(nStartRow, nEndRow, bFiltered);
4536 }
4537 
4538 SCROW ScDocument::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4539 {
4540     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4541         return ::std::numeric_limits<SCROW>::max();
4542 
4543     return maTabs[nTab]->FirstNonFilteredRow(nStartRow, nEndRow);
4544 }
4545 
4546 SCROW ScDocument::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4547 {
4548     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4549         return ::std::numeric_limits<SCROW>::max();
4550 
4551     return maTabs[nTab]->LastNonFilteredRow(nStartRow, nEndRow);
4552 }
4553 
4554 SCROW ScDocument::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4555 {
4556     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4557         return 0;
4558 
4559     return maTabs[nTab]->CountNonFilteredRows(nStartRow, nEndRow);
4560 }
4561 
4562 bool ScDocument::IsManualRowHeight(SCROW nRow, SCTAB nTab) const
4563 {
4564     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4565         return false;
4566 
4567     return maTabs[nTab]->IsManualRowHeight(nRow);
4568 }
4569 
4570 void ScDocument::SyncColRowFlags()
4571 {
4572     for (const auto& a : maTabs)
4573     {
4574         if (a)
4575             a->SyncColRowFlags();
4576     }
4577 }
4578 
4579 SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const
4580 {
4581     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4582         return maTabs[nTab]->GetLastFlaggedRow();
4583     return 0;
4584 }
4585 
4586 SCCOL ScDocument::GetLastChangedCol( SCTAB nTab ) const
4587 {
4588     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4589         return maTabs[nTab]->GetLastChangedCol();
4590     return 0;
4591 }
4592 
4593 SCROW ScDocument::GetLastChangedRow( SCTAB nTab ) const
4594 {
4595     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4596         return maTabs[nTab]->GetLastChangedRow();
4597     return 0;
4598 }
4599 
4600 SCCOL ScDocument::GetNextDifferentChangedCol( SCTAB nTab, SCCOL nStart) const
4601 {
4602     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4603     {
4604         CRFlags nStartFlags = maTabs[nTab]->GetColFlags(nStart);
4605         sal_uInt16 nStartWidth = maTabs[nTab]->GetOriginalWidth(nStart);
4606         for (SCCOL nCol : maTabs[nTab]->GetColumnsRange( nStart + 1, MAXCOL))
4607         {
4608             if (((nStartFlags & CRFlags::ManualBreak) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::ManualBreak)) ||
4609                 (nStartWidth != maTabs[nTab]->GetOriginalWidth(nCol)) ||
4610                 ((nStartFlags & CRFlags::Hidden) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::Hidden)) )
4611                 return nCol;
4612         }
4613         return MAXCOL+1;
4614     }
4615     return 0;
4616 }
4617 
4618 SCROW ScDocument::GetNextDifferentChangedRow( SCTAB nTab, SCROW nStart) const
4619 {
4620     if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
4621         return 0;
4622 
4623     const ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlagsArray = maTabs[nTab]->GetRowFlagsArray();
4624     if (!pRowFlagsArray)
4625         return 0;
4626 
4627     if (!maTabs[nTab]->mpRowHeights || !maTabs[nTab]->mpHiddenRows)
4628         return 0;
4629 
4630     size_t nIndex;          // ignored
4631     SCROW nFlagsEndRow;
4632     SCROW nHiddenEndRow;
4633     SCROW nHeightEndRow;
4634     CRFlags nFlags;
4635     bool bHidden;
4636     sal_uInt16 nHeight;
4637     CRFlags nStartFlags = nFlags = pRowFlagsArray->GetValue( nStart, nIndex, nFlagsEndRow);
4638     bool bStartHidden = bHidden = maTabs[nTab]->RowHidden( nStart, nullptr, &nHiddenEndRow);
4639     sal_uInt16 nStartHeight = nHeight = maTabs[nTab]->GetRowHeight( nStart, nullptr, &nHeightEndRow, false);
4640     SCROW nRow;
4641     while ((nRow = std::min( nHiddenEndRow, std::min( nFlagsEndRow, nHeightEndRow)) + 1) <= MAXROW)
4642     {
4643         if (nFlagsEndRow < nRow)
4644             nFlags = pRowFlagsArray->GetValue( nRow, nIndex, nFlagsEndRow);
4645         if (nHiddenEndRow < nRow)
4646             bHidden = maTabs[nTab]->RowHidden( nRow, nullptr, &nHiddenEndRow);
4647         if (nHeightEndRow < nRow)
4648             nHeight = maTabs[nTab]->GetRowHeight( nRow, nullptr, &nHeightEndRow, false);
4649 
4650         if (((nStartFlags & CRFlags::ManualBreak) != (nFlags & CRFlags::ManualBreak)) ||
4651             ((nStartFlags & CRFlags::ManualSize) != (nFlags & CRFlags::ManualSize)) ||
4652             (bStartHidden != bHidden) ||
4653             (nStartHeight != nHeight))
4654             return nRow;
4655     }
4656 
4657     return MAXROW+1;
4658 }
4659 
4660 void ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault)
4661 {
4662     nDefault = 0;
4663     ScDocAttrIterator aDocAttrItr(this, nTab, nCol, 0, nCol, nLastRow);
4664     SCCOL nColumn;
4665     SCROW nStartRow;
4666     SCROW nEndRow;
4667     const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
4668     if (nEndRow < nLastRow)
4669     {
4670         ScDefaultAttrSet aSet;
4671         ScDefaultAttrSet::iterator aItr = aSet.end();
4672         while (pAttr)
4673         {
4674             ScDefaultAttr aAttr(pAttr);
4675             aItr = aSet.find(aAttr);
4676             if (aItr == aSet.end())
4677             {
4678                 aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
4679                 aAttr.nFirst = nStartRow;
4680                 aSet.insert(aAttr);
4681             }
4682             else
4683             {
4684                 aAttr.nCount = aItr->nCount + static_cast<SCSIZE>(nEndRow - nStartRow + 1);
4685                 aAttr.nFirst = aItr->nFirst;
4686                 aSet.erase(aItr);
4687                 aSet.insert(aAttr);
4688             }
4689             pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
4690         }
4691         ScDefaultAttrSet::iterator aDefaultItr = aSet.begin();
4692         aItr = aDefaultItr;
4693         ++aItr;
4694         while (aItr != aSet.end())
4695         {
4696             // for entries with equal count, use the one with the lowest start row,
4697             // don't use the random order of pointer comparisons
4698             if ( aItr->nCount > aDefaultItr->nCount ||
4699                  ( aItr->nCount == aDefaultItr->nCount && aItr->nFirst < aDefaultItr->nFirst ) )
4700                 aDefaultItr = aItr;
4701             ++aItr;
4702         }
4703         nDefault = aDefaultItr->nFirst;
4704     }
4705 }
4706 
4707 void ScDocument::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
4708 {
4709     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4710         maTabs[nTab]->StripHidden( rX1, rY1, rX2, rY2 );
4711 }
4712 
4713 void ScDocument::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
4714 {
4715     if ( ValidTab(nTab) && maTabs[nTab] )
4716         maTabs[nTab]->ExtendHidden( rX1, rY1, rX2, rY2 );
4717 }
4718 
4719 //  Attribute   ----------------------------------------------------------
4720 
4721 const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
4722 {
4723     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] &&
4724          nCol < maTabs[nTab]->GetAllocatedColumnsCount())
4725     {
4726         const SfxPoolItem* pTemp = maTabs[nTab]->GetAttr( nCol, nRow, nWhich );
4727         if (pTemp)
4728             return pTemp;
4729         else
4730         {
4731             OSL_FAIL( "Attribute Null" );
4732         }
4733     }
4734     return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich );
4735 }
4736 
4737 const SfxPoolItem* ScDocument::GetAttr( const ScAddress& rPos, sal_uInt16 nWhich ) const
4738 {
4739     return GetAttr(rPos.Col(), rPos.Row(), rPos.Tab(), nWhich);
4740 }
4741 
4742 const ScPatternAttr* ScDocument::GetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
4743 {
4744     if (TableExists(nTab))
4745         return maTabs[nTab]->GetPattern( nCol, nRow );
4746     return nullptr;
4747 }
4748 
4749 const ScPatternAttr* ScDocument::GetPattern( const ScAddress& rPos ) const
4750 {
4751     if (TableExists(rPos.Tab()))
4752         return maTabs[rPos.Tab()]->GetPattern(rPos.Col(), rPos.Row());
4753 
4754     return nullptr;
4755 }
4756 
4757 const ScPatternAttr* ScDocument::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const
4758 {
4759     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4760         return maTabs[nTab]->GetMostUsedPattern( nCol, nStartRow, nEndRow );
4761     return nullptr;
4762 }
4763 
4764 void ScDocument::ApplyAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem& rAttr )
4765 {
4766     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4767         maTabs[nTab]->ApplyAttr( nCol, nRow, rAttr );
4768 }
4769 
4770 void ScDocument::ApplyPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
4771 {
4772     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4773         maTabs[nTab]->ApplyPattern( nCol, nRow, rAttr );
4774 }
4775 
4776 void ScDocument::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow,
4777                         SCCOL nEndCol, SCROW nEndRow,
4778                         const ScMarkData& rMark,
4779                         const ScPatternAttr& rAttr,
4780                         ScEditDataArray* pDataArray,
4781                         bool* const pIsChanged )
4782 {
4783     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4784     for (const auto& rTab : rMark)
4785     {
4786         if (rTab >= nMax)
4787             break;
4788         if (maTabs[rTab])
4789             maTabs[rTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr, pDataArray, pIsChanged );
4790     }
4791 }
4792 
4793 void ScDocument::ApplyPatternAreaTab( SCCOL nStartCol, SCROW nStartRow,
4794                         SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScPatternAttr& rAttr )
4795 {
4796     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
4797         if (maTabs[nTab])
4798             maTabs[nTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr );
4799 }
4800 
4801 void ScDocument::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
4802         const ScMarkData& rMark, const ScPatternAttr& rPattern, SvNumFormatType nNewType )
4803 {
4804     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4805     for (const auto& rTab : rMark)
4806     {
4807         if (rTab >= nMax)
4808             break;
4809         if (maTabs[rTab])
4810             maTabs[rTab]->ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType );
4811     }
4812 }
4813 
4814 void ScDocument::AddCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
4815 {
4816     if(static_cast<size_t>(nTab) >= maTabs.size())
4817         return;
4818 
4819     if(!maTabs[nTab])
4820         return;
4821 
4822     maTabs[nTab]->AddCondFormatData(rRange, nIndex);
4823 }
4824 
4825 void ScDocument::RemoveCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
4826 {
4827     if(static_cast<size_t>(nTab) >= maTabs.size())
4828         return;
4829 
4830     if(!maTabs[nTab])
4831         return;
4832 
4833     maTabs[nTab]->RemoveCondFormatData(rRange, nIndex);
4834 }
4835 
4836 void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle)
4837 {
4838     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
4839         if (maTabs[nTab])
4840             maTabs[nTab]->ApplyStyle( nCol, nRow, &rStyle );
4841 }
4842 
4843 void ScDocument::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow,
4844                         SCCOL nEndCol, SCROW nEndRow,
4845                         const ScMarkData& rMark,
4846                         const ScStyleSheet& rStyle)
4847 {
4848     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4849     for (const auto& rTab : rMark)
4850     {
4851         if (rTab >= nMax)
4852             break;
4853         if (maTabs[rTab])
4854             maTabs[rTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
4855     }
4856 }
4857 
4858 void ScDocument::ApplyStyleAreaTab( SCCOL nStartCol, SCROW nStartRow,
4859                         SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScStyleSheet& rStyle)
4860 {
4861     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
4862         if (maTabs[nTab])
4863             maTabs[nTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
4864 }
4865 
4866 void ScDocument::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
4867 {
4868     // ApplySelectionStyle needs multi mark
4869     if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
4870     {
4871         ScRange aRange;
4872         rMark.GetMarkArea( aRange );
4873         ApplyStyleArea( aRange.aStart.Col(), aRange.aStart.Row(),
4874                           aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rStyle );
4875     }
4876     else
4877     {
4878         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4879         for (const auto& rTab : rMark)
4880         {
4881             if (rTab >= nMax)
4882                 break;
4883             if ( maTabs[rTab] )
4884                 maTabs[rTab]->ApplySelectionStyle( rStyle, rMark );
4885         }
4886     }
4887 }
4888 
4889 void ScDocument::ApplySelectionLineStyle( const ScMarkData& rMark,
4890                     const SvxBorderLine* pLine, bool bColorOnly )
4891 {
4892     if ( bColorOnly && !pLine )
4893         return;
4894 
4895     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4896     for (const auto& rTab : rMark)
4897     {
4898         if (rTab >= nMax)
4899             break;
4900         if (maTabs[rTab])
4901             maTabs[rTab]->ApplySelectionLineStyle( rMark, pLine, bColorOnly );
4902     }
4903 }
4904 
4905 const ScStyleSheet* ScDocument::GetStyle( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
4906 {
4907     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
4908         return maTabs[nTab]->GetStyle(nCol, nRow);
4909     else
4910         return nullptr;
4911 }
4912 
4913 const ScStyleSheet* ScDocument::GetSelectionStyle( const ScMarkData& rMark ) const
4914 {
4915     bool    bEqual = true;
4916     bool    bFound;
4917 
4918     const ScStyleSheet* pStyle = nullptr;
4919     const ScStyleSheet* pNewStyle;
4920 
4921     if ( rMark.IsMultiMarked() )
4922     {
4923         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
4924         for (const auto& rTab : rMark)
4925         {
4926             if (rTab >= nMax)
4927                 break;
4928 
4929             if (maTabs[rTab])
4930             {
4931                 pNewStyle = maTabs[rTab]->GetSelectionStyle( rMark, bFound );
4932                 if (bFound)
4933                 {
4934                     if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
4935                         bEqual = false;                            // different
4936                     pStyle = pNewStyle;
4937                 }
4938             }
4939         }
4940     }
4941     if ( rMark.IsMarked() )
4942     {
4943         ScRange aRange;
4944         rMark.GetMarkArea( aRange );
4945         for (SCTAB i=aRange.aStart.Tab(); i<=aRange.aEnd.Tab() && bEqual && i < static_cast<SCTAB>(maTabs.size()); i++)
4946             if (maTabs[i] && rMark.GetTableSelect(i))
4947             {
4948                 pNewStyle = maTabs[i]->GetAreaStyle( bFound,
4949                                         aRange.aStart.Col(), aRange.aStart.Row(),
4950                                         aRange.aEnd.Col(),   aRange.aEnd.Row()   );
4951                 if (bFound)
4952                 {
4953                     if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
4954                         bEqual = false;                                // different
4955                     pStyle = pNewStyle;
4956                 }
4957             }
4958     }
4959 
4960     return bEqual ? pStyle : nullptr;
4961 }
4962 
4963 void ScDocument::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved,
4964                                     OutputDevice* pDev,
4965                                     double nPPTX, double nPPTY,
4966                                     const Fraction& rZoomX, const Fraction& rZoomY )
4967 {
4968     for (const auto& a : maTabs)
4969     {
4970         if (a)
4971             a->StyleSheetChanged
4972                 ( pStyleSheet, bRemoved, pDev, nPPTX, nPPTY, rZoomX, rZoomY );
4973     }
4974 
4975     if ( pStyleSheet && pStyleSheet->GetName() == ScResId(STR_STYLENAME_STANDARD) )
4976     {
4977         //  update attributes for all note objects
4978         ScDetectiveFunc::UpdateAllComments( *this );
4979     }
4980 }
4981 
4982 bool ScDocument::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
4983 {
4984     if ( bStyleSheetUsageInvalid || rStyle.GetUsage() == ScStyleSheet::Usage::UNKNOWN )
4985     {
4986         SfxStyleSheetIterator aIter( mxPoolHelper->GetStylePool(),
4987                     SfxStyleFamily::Para );
4988         for ( const SfxStyleSheetBase* pStyle = aIter.First(); pStyle;
4989                                        pStyle = aIter.Next() )
4990         {
4991             if (pStyle->isScStyleSheet())
4992             {
4993                 const ScStyleSheet* pScStyle = static_cast<const ScStyleSheet*>( pStyle  );
4994                 pScStyle->SetUsage( ScStyleSheet::Usage::NOTUSED );
4995             }
4996         }
4997 
4998         bool bIsUsed = false;
4999 
5000         for (const auto& a : maTabs)
5001         {
5002             if (a && a->IsStyleSheetUsed( rStyle ) )
5003                 bIsUsed = true;
5004         }
5005 
5006         bStyleSheetUsageInvalid = false;
5007 
5008         return bIsUsed;
5009     }
5010 
5011     return rStyle.GetUsage() == ScStyleSheet::Usage::USED;
5012 }
5013 
5014 bool ScDocument::ApplyFlagsTab( SCCOL nStartCol, SCROW nStartRow,
5015                         SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
5016 {
5017     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
5018         if (maTabs[nTab])
5019             return maTabs[nTab]->ApplyFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags );
5020 
5021     OSL_FAIL("ApplyFlags: wrong table");
5022     return false;
5023 }
5024 
5025 bool ScDocument::RemoveFlagsTab( SCCOL nStartCol, SCROW nStartRow,
5026                         SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
5027 {
5028     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
5029         if (maTabs[nTab])
5030             return maTabs[nTab]->RemoveFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags );
5031 
5032     OSL_FAIL("RemoveFlags: wrong table");
5033     return false;
5034 }
5035 
5036 void ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
5037 {
5038     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
5039         if (maTabs[nTab])
5040             maTabs[nTab]->SetPattern( nCol, nRow, rAttr );
5041 }
5042 
5043 void ScDocument::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr )
5044 {
5045     SCTAB nTab = rPos.Tab();
5046     if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
5047         maTabs[nTab]->SetPattern( rPos, rAttr );
5048 }
5049 
5050 std::unique_ptr<ScPatternAttr> ScDocument::CreateSelectionPattern( const ScMarkData& rMark, bool bDeep )
5051 {
5052     ScMergePatternState aState;
5053 
5054     if ( rMark.IsMultiMarked() )                                // multi selection
5055     {
5056         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5057         for (const auto& rTab : rMark)
5058         {
5059             if (rTab >= nMax)
5060                 break;
5061             if (maTabs[rTab])
5062                 maTabs[rTab]->MergeSelectionPattern( aState, rMark, bDeep );
5063         }
5064     }
5065     if ( rMark.IsMarked() )                                     // single selection
5066     {
5067         ScRange aRange;
5068         rMark.GetMarkArea(aRange);
5069         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5070         for (const auto& rTab : rMark)
5071         {
5072             if (rTab >= nMax)
5073                 break;
5074             if (maTabs[rTab])
5075                 maTabs[rTab]->MergePatternArea( aState,
5076                                 aRange.aStart.Col(), aRange.aStart.Row(),
5077                                 aRange.aEnd.Col(), aRange.aEnd.Row(), bDeep );
5078         }
5079     }
5080 
5081     OSL_ENSURE( aState.pItemSet, "SelectionPattern Null" );
5082     if (aState.pItemSet)
5083     {
5084         std::unique_ptr<ScPatternAttr> pPattern(new ScPatternAttr( std::move(aState.pItemSet) ));
5085         if (aState.mbValidPatternId)
5086             pPattern->SetKey(aState.mnPatternId);
5087 
5088         return pPattern;
5089     }
5090     else
5091         return std::unique_ptr<ScPatternAttr>(new ScPatternAttr( GetPool() )); // empty
5092 }
5093 
5094 const ScPatternAttr* ScDocument::GetSelectionPattern( const ScMarkData& rMark )
5095 {
5096     pSelectionAttr = CreateSelectionPattern( rMark );
5097     return pSelectionAttr.get();
5098 }
5099 
5100 void ScDocument::GetSelectionFrame( const ScMarkData& rMark,
5101                                     SvxBoxItem&     rLineOuter,
5102                                     SvxBoxInfoItem& rLineInner )
5103 {
5104     rLineOuter.SetLine(nullptr, SvxBoxItemLine::TOP);
5105     rLineOuter.SetLine(nullptr, SvxBoxItemLine::BOTTOM);
5106     rLineOuter.SetLine(nullptr, SvxBoxItemLine::LEFT);
5107     rLineOuter.SetLine(nullptr, SvxBoxItemLine::RIGHT);
5108     rLineOuter.SetAllDistances(0);
5109 
5110     rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::HORI);
5111     rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::VERT);
5112     rLineInner.SetTable(true);
5113     rLineInner.SetDist(true);
5114     rLineInner.SetMinDist(false);
5115 
5116     ScLineFlags aFlags;
5117 
5118     if( rMark.IsMultiMarked() )
5119     {
5120         ScRangeList aRangeList;
5121         rMark.FillRangeListWithMarks( &aRangeList, false );
5122         size_t nRangeCount = aRangeList.size();
5123         bool bMultipleRows = false, bMultipleCols = false;
5124         for( size_t nRangeIdx = 0; nRangeIdx < nRangeCount; ++nRangeIdx )
5125         {
5126             const ScRange & rRange = aRangeList[ nRangeIdx ];
5127             bMultipleRows = ( bMultipleRows || ( rRange.aStart.Row() != rRange.aEnd.Row() ) );
5128             bMultipleCols = ( bMultipleCols || ( rRange.aStart.Col() != rRange.aEnd.Col() ) );
5129             SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5130             for (const auto& rTab : rMark)
5131             {
5132                 if (rTab >= nMax)
5133                     break;
5134 
5135                 if (maTabs[rTab])
5136                     maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
5137                                           rRange.aStart.Col(), rRange.aStart.Row(),
5138                                           rRange.aEnd.Col(),   rRange.aEnd.Row() );
5139             }
5140         }
5141         rLineInner.EnableHor( bMultipleRows );
5142         rLineInner.EnableVer( bMultipleCols );
5143     }
5144     else if( rMark.IsMarked() )
5145     {
5146         ScRange aRange;
5147         rMark.GetMarkArea(aRange);
5148         rLineInner.EnableHor( aRange.aStart.Row() != aRange.aEnd.Row() );
5149         rLineInner.EnableVer( aRange.aStart.Col() != aRange.aEnd.Col() );
5150         SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5151         for (const auto& rTab : rMark)
5152         {
5153             if (rTab >= nMax)
5154                 break;
5155 
5156             if (maTabs[rTab])
5157                 maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
5158                                           aRange.aStart.Col(), aRange.aStart.Row(),
5159                                           aRange.aEnd.Col(),   aRange.aEnd.Row() );
5160         }
5161     }
5162 
5163         // Evaluate don't care Status
5164 
5165     rLineInner.SetValid( SvxBoxInfoItemValidFlags::LEFT,   ( aFlags.nLeft != SC_LINE_DONTCARE ) );
5166     rLineInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT,  ( aFlags.nRight != SC_LINE_DONTCARE ) );
5167     rLineInner.SetValid( SvxBoxInfoItemValidFlags::TOP,    ( aFlags.nTop != SC_LINE_DONTCARE ) );
5168     rLineInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, ( aFlags.nBottom != SC_LINE_DONTCARE ) );
5169     rLineInner.SetValid( SvxBoxInfoItemValidFlags::HORI,   ( aFlags.nHori != SC_LINE_DONTCARE ) );
5170     rLineInner.SetValid( SvxBoxInfoItemValidFlags::VERT,   ( aFlags.nVert != SC_LINE_DONTCARE ) );
5171 }
5172 
5173 bool ScDocument::HasAttrib( SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
5174                             SCCOL nCol2, SCROW nRow2, SCTAB nTab2, HasAttrFlags nMask ) const
5175 {
5176     if ( nMask & HasAttrFlags::Rotate )
5177     {
5178         //  Is attribute used in document?
5179         //  (as in fillinfo)
5180 
5181         ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
5182 
5183         bool bAnyItem = false;
5184         for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_ROTATE_VALUE))
5185         {
5186             // 90 or 270 degrees is former SvxOrientationItem - only look for other values
5187             // (see ScPatternAttr::GetCellOrientation)
5188             sal_Int32 nAngle = static_cast<const SfxInt32Item*>(pItem)->GetValue();
5189             if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 )
5190             {
5191                 bAnyItem = true;
5192                 break;
5193             }
5194         }
5195         if (!bAnyItem)
5196             nMask &= ~HasAttrFlags::Rotate;
5197     }
5198 
5199     if (nMask == HasAttrFlags::NONE)
5200         return false;
5201 
5202     bool bFound = false;
5203     for (SCTAB i=nTab1; i<=nTab2 && !bFound && i < static_cast<SCTAB>(maTabs.size()); i++)
5204         if (maTabs[i])
5205         {
5206             if ( nMask & HasAttrFlags::RightOrCenter )
5207             {
5208                 //  On a RTL sheet, don't start to look for the default left value
5209                 //  (which is then logically right), instead always assume true.
5210                 //  That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets.
5211 
5212                 if ( IsLayoutRTL(i) )
5213                     bFound = true;
5214             }
5215 
5216             if ( !bFound )
5217                 bFound = maTabs[i]->HasAttrib( nCol1, nRow1, nCol2, nRow2, nMask );
5218         }
5219 
5220     return bFound;
5221 }
5222 
5223 bool ScDocument::HasAttrib( const ScRange& rRange, HasAttrFlags nMask ) const
5224 {
5225     return HasAttrib( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
5226                       rRange.aEnd.Col(),   rRange.aEnd.Row(),   rRange.aEnd.Tab(),
5227                       nMask );
5228 }
5229 
5230 void ScDocument::FindMaxRotCol( SCTAB nTab, RowInfo* pRowInfo, SCSIZE nArrCount,
5231                                 SCCOL nX1, SCCOL nX2 ) const
5232 {
5233     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
5234         maTabs[nTab]->FindMaxRotCol( pRowInfo, nArrCount, nX1, nX2 );
5235     else
5236     {
5237         OSL_FAIL("FindMaxRotCol: wrong table");
5238     }
5239 }
5240 
5241 void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab,
5242                         const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop,
5243                         const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const
5244 {
5245     //TODO: consider page limits for printing !!!!!
5246 
5247     const SvxBoxItem* pThisAttr = GetEffItem( nCol, nRow, nTab, ATTR_BORDER );
5248     OSL_ENSURE(pThisAttr,"where is the attribute?");
5249 
5250     const SvxBorderLine* pLeftLine   = pThisAttr->GetLeft();
5251     const SvxBorderLine* pTopLine    = pThisAttr->GetTop();
5252     const SvxBorderLine* pRightLine  = pThisAttr->GetRight();
5253     const SvxBorderLine* pBottomLine = pThisAttr->GetBottom();
5254 
5255     if ( nCol > 0 )
5256     {
5257         const SvxBorderLine* pOther = GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER )->GetRight();
5258         if ( ScHasPriority( pOther, pLeftLine ) )
5259             pLeftLine = pOther;
5260     }
5261     if ( nRow > 0 )
5262     {
5263         const SvxBorderLine* pOther = GetEffItem( nCol, nRow-1, nTab, ATTR_BORDER )->GetBottom();
5264         if ( ScHasPriority( pOther, pTopLine ) )
5265             pTopLine = pOther;
5266     }
5267     if ( nCol < MAXCOL )
5268     {
5269         const SvxBorderLine* pOther = GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER )->GetLeft();
5270         if ( ScHasPriority( pOther, pRightLine ) )
5271             pRightLine = pOther;
5272     }
5273     if ( nRow < MAXROW )
5274     {
5275         const SvxBorderLine* pOther = GetEffItem( nCol, nRow+1, nTab, ATTR_BORDER )->GetTop();
5276         if ( ScHasPriority( pOther, pBottomLine ) )
5277             pBottomLine = pOther;
5278     }
5279 
5280     if (ppLeft)
5281         *ppLeft = pLeftLine;
5282     if (ppTop)
5283         *ppTop = pTopLine;
5284     if (ppRight)
5285         *ppRight = pRightLine;
5286     if (ppBottom)
5287         *ppBottom = pBottomLine;
5288 }
5289 
5290 bool ScDocument::IsBlockEmpty( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow,
5291                                         SCCOL nEndCol, SCROW nEndRow, bool bIgnoreNotes ) const
5292 {
5293     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
5294         if (maTabs[nTab])
5295             return maTabs[nTab]->IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow, bIgnoreNotes );
5296 
5297     OSL_FAIL("wrong table number");
5298     return false;
5299 }
5300 
5301 void ScDocument::LockTable(SCTAB nTab)
5302 {
5303     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
5304         maTabs[nTab]->LockTable();
5305     else
5306     {
5307         OSL_FAIL("wrong table number");
5308     }
5309 }
5310 
5311 void ScDocument::UnlockTable(SCTAB nTab)
5312 {
5313     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
5314         maTabs[nTab]->UnlockTable();
5315     else
5316     {
5317         OSL_FAIL("wrong table number");
5318     }
5319 }
5320 
5321 bool ScDocument::IsBlockEditable( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow,
5322                                         SCCOL nEndCol, SCROW nEndRow,
5323                                         bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
5324 {
5325     // import into read-only document is possible
5326     if (!bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly())
5327     {
5328         if ( pOnlyNotBecauseOfMatrix )
5329             *pOnlyNotBecauseOfMatrix = false;
5330         return false;
5331     }
5332 
5333     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
5334         if (maTabs[nTab])
5335             return maTabs[nTab]->IsBlockEditable( nStartCol, nStartRow, nEndCol,
5336                 nEndRow, pOnlyNotBecauseOfMatrix );
5337 
5338     OSL_FAIL("wrong table number");
5339     if ( pOnlyNotBecauseOfMatrix )
5340         *pOnlyNotBecauseOfMatrix = false;
5341     return false;
5342 }
5343 
5344 bool ScDocument::IsSelectionEditable( const ScMarkData& rMark,
5345             bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
5346 {
5347     // import into read-only document is possible
5348     if ( !bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly() )
5349     {
5350         if ( pOnlyNotBecauseOfMatrix )
5351             *pOnlyNotBecauseOfMatrix = false;
5352         return false;
5353     }
5354 
5355     ScRange aRange;
5356     rMark.GetMarkArea(aRange);
5357 
5358     bool bOk = true;
5359     bool bMatrix = ( pOnlyNotBecauseOfMatrix != nullptr );
5360     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5361     for (const auto& rTab : rMark)
5362     {
5363         if (rTab >= nMax)
5364             break;
5365 
5366         if ( maTabs[rTab] )
5367         {
5368             if (rMark.IsMarked())
5369             {
5370                 if ( !maTabs[rTab]->IsBlockEditable( aRange.aStart.Col(),
5371                         aRange.aStart.Row(), aRange.aEnd.Col(),
5372                         aRange.aEnd.Row(), pOnlyNotBecauseOfMatrix ) )
5373                 {
5374                     bOk = false;
5375                     if ( pOnlyNotBecauseOfMatrix )
5376                         bMatrix = *pOnlyNotBecauseOfMatrix;
5377                 }
5378             }
5379             if (rMark.IsMultiMarked())
5380             {
5381                 if ( !maTabs[rTab]->IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix ) )
5382                 {
5383                     bOk = false;
5384                     if ( pOnlyNotBecauseOfMatrix )
5385                         bMatrix = *pOnlyNotBecauseOfMatrix;
5386                 }
5387             }
5388         }
5389 
5390         if (!bOk && !bMatrix)
5391             break;
5392     }
5393 
5394     if ( pOnlyNotBecauseOfMatrix )
5395         *pOnlyNotBecauseOfMatrix = ( !bOk && bMatrix );
5396 
5397     return bOk;
5398 }
5399 
5400 bool ScDocument::HasSelectedBlockMatrixFragment( SCCOL nStartCol, SCROW nStartRow,
5401                                 SCCOL nEndCol, SCROW nEndRow,
5402                                 const ScMarkData& rMark ) const
5403 {
5404     bool bOk = true;
5405     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5406     for (const auto& rTab : rMark)
5407     {
5408         if (rTab >= nMax)
5409             break;
5410 
5411         if (maTabs[rTab] && maTabs[rTab]->HasBlockMatrixFragment( nStartCol, nStartRow, nEndCol, nEndRow ))
5412             bOk = false;
5413 
5414         if (!bOk)
5415             break;
5416     }
5417 
5418     return !bOk;
5419 }
5420 
5421 bool ScDocument::GetMatrixFormulaRange( const ScAddress& rCellPos, ScRange& rMatrix )
5422 {
5423     //  if rCell is part of a matrix formula, return its complete range
5424 
5425     ScFormulaCell* pFCell = GetFormulaCell(rCellPos);
5426     if (!pFCell)
5427         // not a formula cell.  Bail out.
5428         return false;
5429 
5430     ScAddress aOrigin = rCellPos;
5431     if (!pFCell->GetMatrixOrigin(aOrigin))
5432         // Failed to get the address of the matrix origin.
5433         return false;
5434 
5435     if (aOrigin != rCellPos)
5436     {
5437         pFCell = GetFormulaCell(aOrigin);
5438         if (!pFCell)
5439             // The matrix origin cell is not a formula cell !?  Something is up...
5440             return false;
5441     }
5442 
5443     SCCOL nSizeX;
5444     SCROW nSizeY;
5445     pFCell->GetMatColsRows(nSizeX, nSizeY);
5446     if (nSizeX <= 0 || nSizeY <= 0)
5447     {
5448         // GetMatrixEdge computes also dimensions of the matrix
5449         // if not already done (may occur if document is loaded
5450         // from old file format).
5451         // Needs an "invalid" initialized address.
5452         aOrigin.SetInvalid();
5453         pFCell->GetMatrixEdge(aOrigin);
5454         pFCell->GetMatColsRows(nSizeX, nSizeY);
5455     }
5456 
5457     if (nSizeX <= 0 || nSizeY <= 0)
5458         // Matrix size is still invalid. Give up.
5459         return false;
5460 
5461     ScAddress aEnd( aOrigin.Col() + nSizeX - 1,
5462                     aOrigin.Row() + nSizeY - 1,
5463                     aOrigin.Tab() );
5464 
5465     rMatrix.aStart = aOrigin;
5466     rMatrix.aEnd = aEnd;
5467 
5468     return true;
5469 }
5470 
5471 void ScDocument::ExtendOverlapped( SCCOL& rStartCol, SCROW& rStartRow,
5472                                 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) const
5473 {
5474     if ( ValidColRow(rStartCol,rStartRow) && ValidColRow(nEndCol,nEndRow) && ValidTab(nTab) )
5475     {
5476         if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
5477         {
5478             SCCOL nCol;
5479             SCCOL nOldCol = rStartCol;
5480             SCROW nOldRow = rStartRow;
5481             for (nCol=nOldCol; nCol<=nEndCol; nCol++)
5482                 while (GetAttr(nCol,rStartRow,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped())
5483                     --rStartRow;
5484 
5485             //TODO: pass on ?
5486 
5487             ScAttrArray* pAttrArray = maTabs[nTab]->aCol[nOldCol].pAttrArray.get();
5488             SCSIZE nIndex;
5489             if ( pAttrArray->Count() )
5490                 pAttrArray->Search( nOldRow, nIndex );
5491             else
5492                 nIndex = 0;
5493             SCROW nAttrPos = nOldRow;
5494             while (nAttrPos<=nEndRow)
5495             {
5496                 OSL_ENSURE( nIndex < pAttrArray->Count(), "Wrong index in AttrArray" );
5497 
5498                 bool bHorOverlapped;
5499                 if ( pAttrArray->Count() )
5500                     bHorOverlapped = pAttrArray->mvData[nIndex].pPattern->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
5501                 else
5502                     bHorOverlapped = GetDefPattern()->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
5503                 if ( bHorOverlapped )
5504                 {
5505                     SCROW nEndRowSeg = (pAttrArray->Count()) ? pAttrArray->mvData[nIndex].nEndRow : MAXROW;
5506                     SCROW nLoopEndRow = std::min( nEndRow, nEndRowSeg );
5507                     for (SCROW nAttrRow = nAttrPos; nAttrRow <= nLoopEndRow; nAttrRow++)
5508                     {
5509                         SCCOL nTempCol = nOldCol;
5510                         do
5511                             --nTempCol;
5512                         while (GetAttr(nTempCol,nAttrRow,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped());
5513                         if (nTempCol < rStartCol)
5514                             rStartCol = nTempCol;
5515                     }
5516                 }
5517                 if ( pAttrArray->Count() )
5518                 {
5519                     nAttrPos = pAttrArray->mvData[nIndex].nEndRow + 1;
5520                     ++nIndex;
5521                 }
5522                 else
5523                     nAttrPos = MAXROW + 1;
5524             }
5525         }
5526     }
5527     else
5528     {
5529         OSL_FAIL("ExtendOverlapped: invalid range");
5530     }
5531 }
5532 
5533 void ScDocument::ExtendMergeSel( SCCOL nStartCol, SCROW nStartRow,
5534                               SCCOL& rEndCol, SCROW& rEndRow,
5535                               const ScMarkData& rMark, bool bRefresh )
5536 {
5537     // use all selected sheets from rMark
5538 
5539     SCCOL nOldEndCol = rEndCol;
5540     SCROW nOldEndRow = rEndRow;
5541 
5542     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5543     for (const auto& rTab : rMark)
5544     {
5545         if (rTab >= nMax)
5546             break;
5547 
5548         if ( maTabs[rTab] )
5549         {
5550             SCCOL nThisEndCol = nOldEndCol;
5551             SCROW nThisEndRow = nOldEndRow;
5552             ExtendMerge( nStartCol, nStartRow, nThisEndCol, nThisEndRow, rTab, bRefresh );
5553             if ( nThisEndCol > rEndCol )
5554                 rEndCol = nThisEndCol;
5555             if ( nThisEndRow > rEndRow )
5556                 rEndRow = nThisEndRow;
5557         }
5558     }
5559 }
5560 
5561 bool ScDocument::ExtendMerge( SCCOL nStartCol, SCROW nStartRow,
5562                               SCCOL& rEndCol,  SCROW& rEndRow,
5563                               SCTAB nTab, bool bRefresh )
5564 {
5565     bool bFound = false;
5566     if ( ValidColRow(nStartCol,nStartRow) && ValidColRow(rEndCol,rEndRow) && ValidTab(nTab) )
5567     {
5568         if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
5569             bFound = maTabs[nTab]->ExtendMerge( nStartCol, nStartRow, rEndCol, rEndRow, bRefresh );
5570 
5571         if (bRefresh)
5572             RefreshAutoFilter( nStartCol, nStartRow, rEndCol, rEndRow, nTab );
5573     }
5574     else
5575     {
5576         OSL_FAIL("ExtendMerge: invalid range");
5577     }
5578 
5579     return bFound;
5580 }
5581 
5582 bool ScDocument::ExtendMerge( ScRange& rRange, bool bRefresh )
5583 {
5584     bool bFound = false;
5585     SCTAB nStartTab = rRange.aStart.Tab();
5586     SCTAB nEndTab   = rRange.aEnd.Tab();
5587     SCCOL nEndCol   = rRange.aEnd.Col();
5588     SCROW nEndRow   = rRange.aEnd.Row();
5589 
5590     PutInOrder( nStartTab, nEndTab );
5591     for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
5592     {
5593         SCCOL nExtendCol = rRange.aEnd.Col();
5594         SCROW nExtendRow = rRange.aEnd.Row();
5595         if (ExtendMerge( rRange.aStart.Col(), rRange.aStart.Row(),
5596                          nExtendCol,          nExtendRow,
5597                          nTab, bRefresh ) )
5598         {
5599             bFound = true;
5600             if (nExtendCol > nEndCol) nEndCol = nExtendCol;
5601             if (nExtendRow > nEndRow) nEndRow = nExtendRow;
5602         }
5603     }
5604 
5605     rRange.aEnd.SetCol(nEndCol);
5606     rRange.aEnd.SetRow(nEndRow);
5607 
5608     return bFound;
5609 }
5610 
5611 void ScDocument::ExtendTotalMerge( ScRange& rRange ) const
5612 {
5613     // Extend range to merged cells without including any new non-overlapped cells
5614     ScRange aExt = rRange;
5615     // ExtendMerge() is non-const, but called without refresh.
5616     if (const_cast<ScDocument*>(this)->ExtendMerge( aExt ))
5617     {
5618         if ( aExt.aEnd.Row() > rRange.aEnd.Row() )
5619         {
5620             ScRange aTest = aExt;
5621             aTest.aStart.SetRow( rRange.aEnd.Row() + 1 );
5622             if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
5623                 aExt.aEnd.SetRow(rRange.aEnd.Row());
5624         }
5625         if ( aExt.aEnd.Col() > rRange.aEnd.Col() )
5626         {
5627             ScRange aTest = aExt;
5628             aTest.aStart.SetCol( rRange.aEnd.Col() + 1 );
5629             if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
5630                 aExt.aEnd.SetCol(rRange.aEnd.Col());
5631         }
5632 
5633         rRange = aExt;
5634     }
5635 }
5636 
5637 void ScDocument::ExtendOverlapped( ScRange& rRange ) const
5638 {
5639     SCTAB nStartTab = rRange.aStart.Tab();
5640     SCTAB nEndTab   = rRange.aEnd.Tab();
5641     SCCOL nStartCol = rRange.aStart.Col();
5642     SCROW nStartRow = rRange.aStart.Row();
5643 
5644     PutInOrder( nStartTab, nEndTab );
5645     for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
5646     {
5647         SCCOL nExtendCol = rRange.aStart.Col();
5648         SCROW nExtendRow = rRange.aStart.Row();
5649         ExtendOverlapped( nExtendCol, nExtendRow,
5650                                 rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
5651         if (nExtendCol < nStartCol)
5652         {
5653             nStartCol = nExtendCol;
5654         }
5655         if (nExtendRow < nStartRow)
5656         {
5657             nStartRow = nExtendRow;
5658         }
5659     }
5660 
5661     rRange.aStart.SetCol(nStartCol);
5662     rRange.aStart.SetRow(nStartRow);
5663 }
5664 
5665 bool ScDocument::RefreshAutoFilter( SCCOL nStartCol, SCROW nStartRow,
5666                                     SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
5667 {
5668     SCTAB nDBTab;
5669     SCCOL nDBStartCol;
5670     SCROW nDBStartRow;
5671     SCCOL nDBEndCol;
5672     SCROW nDBEndRow;
5673 
5674     //      Delete Autofilter
5675 
5676     bool bChange = RemoveFlagsTab( nStartCol,nStartRow, nEndCol,nEndRow, nTab, ScMF::Auto );
5677 
5678     //      Set Autofilter
5679 
5680     const ScDBData* pData = nullptr;
5681     ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
5682     for (const auto& rxDB : rDBs)
5683     {
5684         if (rxDB->HasAutoFilter())
5685         {
5686             rxDB->GetArea(nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow);
5687             if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
5688                                     nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
5689             {
5690                 if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
5691                                     nDBTab, ScMF::Auto ))
5692                     bChange = true;
5693             }
5694         }
5695     }
5696     if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
5697         pData = maTabs[nTab]->GetAnonymousDBData();
5698     else
5699         pData=nullptr;
5700     if (pData && pData->HasAutoFilter())
5701     {
5702         pData->GetArea( nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow );
5703         if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
5704                                 nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
5705         {
5706             if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
5707                                 nDBTab, ScMF::Auto ))
5708                 bChange = true;
5709         }
5710     }
5711     return bChange;
5712 }
5713 
5714 void ScDocument::SkipOverlapped( SCCOL& rCol, SCROW& rRow, SCTAB nTab ) const
5715 {
5716     while (IsHorOverlapped(rCol, rRow, nTab))
5717         --rCol;
5718     while (IsVerOverlapped(rCol, rRow, nTab))
5719         --rRow;
5720 }
5721 
5722 bool ScDocument::IsHorOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
5723 {
5724     const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG );
5725     if (pAttr)
5726         return pAttr->IsHorOverlapped();
5727     else
5728     {
5729         OSL_FAIL("Overlapped: Attr==0");
5730         return false;
5731     }
5732 }
5733 
5734 bool ScDocument::IsVerOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
5735 {
5736     const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG );
5737     if (pAttr)
5738         return pAttr->IsVerOverlapped();
5739     else
5740     {
5741         OSL_FAIL("Overlapped: Attr==0");
5742         return false;
5743     }
5744 }
5745 
5746 void ScDocument::ApplySelectionFrame( const ScMarkData& rMark,
5747                                       const SvxBoxItem& rLineOuter,
5748                                       const SvxBoxInfoItem* pLineInner )
5749 {
5750     ScRangeList aRangeList;
5751     rMark.FillRangeListWithMarks( &aRangeList, false );
5752     size_t nRangeCount = aRangeList.size();
5753     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5754     for (const auto& rTab : rMark)
5755     {
5756         if (rTab >= nMax)
5757             break;
5758 
5759         if (maTabs[rTab])
5760         {
5761             for ( size_t j=0; j < nRangeCount; j++ )
5762             {
5763                 const ScRange & rRange = aRangeList[ j ];
5764                 maTabs[rTab]->ApplyBlockFrame( rLineOuter, pLineInner,
5765                     rRange.aStart.Col(), rRange.aStart.Row(),
5766                     rRange.aEnd.Col(),   rRange.aEnd.Row() );
5767             }
5768         }
5769     }
5770     if (rLineOuter.IsRemoveAdjacentCellBorder())
5771     {
5772         SvxBoxItem aTmp0(rLineOuter);
5773         aTmp0.SetLine( nullptr, SvxBoxItemLine::TOP );
5774         aTmp0.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
5775         aTmp0.SetLine( nullptr, SvxBoxItemLine::LEFT );
5776         aTmp0.SetLine( nullptr, SvxBoxItemLine::RIGHT );
5777         SvxBoxItem aLeft( aTmp0 );
5778         SvxBoxItem aRight( aTmp0 );
5779         SvxBoxItem aTop( aTmp0 );
5780         SvxBoxItem aBottom( aTmp0 );
5781 
5782         SvxBoxInfoItem aTmp1( *pLineInner );
5783         aTmp1.SetTable( false );
5784         aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
5785         aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
5786         aTmp1.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
5787         aTmp1.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
5788         SvxBoxInfoItem aLeftInfo( aTmp1 );
5789         SvxBoxInfoItem aRightInfo( aTmp1 );
5790         SvxBoxInfoItem aTopInfo( aTmp1 );
5791         SvxBoxInfoItem aBottomInfo( aTmp1 );
5792 
5793         if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::TOP ) && !rLineOuter.GetTop())
5794             aTopInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM );
5795 
5796         if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) && !rLineOuter.GetBottom())
5797             aBottomInfo.SetValid( SvxBoxInfoItemValidFlags::TOP );
5798 
5799         if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::LEFT ) && !rLineOuter.GetLeft())
5800             aLeftInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT );
5801 
5802         if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) && !rLineOuter.GetRight())
5803             aRightInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT );
5804 
5805         const ScRangeList& rRangeListTopEnvelope = rMark.GetTopEnvelope();
5806         const ScRangeList& rRangeListBottomEnvelope = rMark.GetBottomEnvelope();
5807         const ScRangeList& rRangeListLeftEnvelope = rMark.GetLeftEnvelope();
5808         const ScRangeList& rRangeListRightEnvelope = rMark.GetRightEnvelope();
5809 
5810         for (const auto& rTab : rMark)
5811         {
5812             if (rTab >= nMax)
5813                 break;
5814 
5815             if ( maTabs[rTab] )
5816             {
5817                 size_t nEnvelopeRangeCount = rRangeListTopEnvelope.size();
5818                 for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
5819                 {
5820                     const ScRange & rRange = rRangeListTopEnvelope[ j ];
5821                     maTabs[rTab]->ApplyBlockFrame( aTop, &aTopInfo,
5822                                                     rRange.aStart.Col(), rRange.aStart.Row(),
5823                                                     rRange.aEnd.Col(),   rRange.aEnd.Row() );
5824                 }
5825                 nEnvelopeRangeCount = rRangeListBottomEnvelope.size();
5826                 for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
5827                 {
5828                     const ScRange & rRange = rRangeListBottomEnvelope[ j ];
5829                     maTabs[rTab]->ApplyBlockFrame( aBottom, &aBottomInfo,
5830                                                     rRange.aStart.Col(), rRange.aStart.Row(),
5831                                                     rRange.aEnd.Col(),   rRange.aEnd.Row() );
5832                 }
5833                 nEnvelopeRangeCount = rRangeListLeftEnvelope.size();
5834                 for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
5835                 {
5836                     const ScRange & rRange = rRangeListLeftEnvelope[ j ];
5837                     maTabs[rTab]->ApplyBlockFrame( aLeft, &aLeftInfo,
5838                                                     rRange.aStart.Col(), rRange.aStart.Row(),
5839                                                     rRange.aEnd.Col(),   rRange.aEnd.Row() );
5840                 }
5841                 nEnvelopeRangeCount = rRangeListRightEnvelope.size();
5842                 for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
5843                 {
5844                     const ScRange & rRange = rRangeListRightEnvelope[ j ];
5845                     maTabs[rTab]->ApplyBlockFrame( aRight, &aRightInfo,
5846                                                     rRange.aStart.Col(), rRange.aStart.Row(),
5847                                                     rRange.aEnd.Col(),   rRange.aEnd.Row() );
5848                 }
5849             }
5850         }
5851     }
5852 }
5853 
5854 void ScDocument::ApplyFrameAreaTab(const ScRange& rRange,
5855                                    const SvxBoxItem& rLineOuter,
5856                                    const SvxBoxInfoItem& rLineInner)
5857 {
5858     SCTAB nStartTab = rRange.aStart.Tab();
5859     SCTAB nEndTab = rRange.aStart.Tab();
5860     for (SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++)
5861         if (maTabs[nTab])
5862             maTabs[nTab]->ApplyBlockFrame(rLineOuter, &rLineInner,
5863                                           rRange.aStart.Col(), rRange.aStart.Row(),
5864                                           rRange.aEnd.Col(),   rRange.aEnd.Row());
5865 }
5866 
5867 void ScDocument::ApplySelectionPattern( const ScPatternAttr& rAttr, const ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged )
5868 {
5869     const SfxItemSet* pSet = &rAttr.GetItemSet();
5870     bool bSet = false;
5871     sal_uInt16 i;
5872     for (i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END && !bSet; i++)
5873         if (pSet->GetItemState(i) == SfxItemState::SET)
5874             bSet = true;
5875 
5876     if (bSet)
5877     {
5878         // ApplySelectionCache needs multi mark
5879         if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
5880         {
5881             ScRange aRange;
5882             rMark.GetMarkArea( aRange );
5883             ApplyPatternArea( aRange.aStart.Col(), aRange.aStart.Row(),
5884                               aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rAttr, pDataArray, pIsChanged );
5885         }
5886         else
5887         {
5888             SfxItemPoolCache aCache( mxPoolHelper->GetDocPool(), pSet );
5889             SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5890             for (const auto& rTab : rMark)
5891             {
5892                 if (rTab >= nMax)
5893                     break;
5894                 if (maTabs[rTab])
5895                     maTabs[rTab]->ApplySelectionCache( &aCache, rMark, pDataArray, pIsChanged );
5896             }
5897         }
5898     }
5899 }
5900 
5901 void ScDocument::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
5902 {
5903     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5904     for (const auto& rTab : rMark)
5905     {
5906         if (rTab >= nMax)
5907             break;
5908         if (maTabs[rTab])
5909             maTabs[rTab]->ChangeSelectionIndent( bIncrement, rMark );
5910     }
5911 }
5912 
5913 void ScDocument::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark )
5914 {
5915     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5916     for (const auto& rTab : rMark)
5917     {
5918         if (rTab >= nMax)
5919             break;
5920         if (maTabs[rTab])
5921             maTabs[rTab]->ClearSelectionItems( pWhich, rMark );
5922     }
5923 }
5924 
5925 void ScDocument::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
5926 {
5927     sc::AutoCalcSwitch aACSwitch(*this, false);
5928 
5929     std::vector<ScAddress> aGroupPos;
5930     // Destroy and reconstruct listeners only if content is affected.
5931     bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
5932     if (bDelContent)
5933     {
5934         // Record the positions of top and/or bottom formula groups that
5935         // intersect the area borders.
5936         sc::EndListeningContext aCxt(*this);
5937         ScRangeList aRangeList;
5938         rMark.FillRangeListWithMarks( &aRangeList, false);
5939         for (size_t i = 0; i < aRangeList.size(); ++i)
5940         {
5941             const ScRange & rRange = aRangeList[i];
5942             EndListeningIntersectedGroups( aCxt, rRange, &aGroupPos);
5943         }
5944         aCxt.purgeEmptyBroadcasters();
5945     }
5946 
5947     SCTAB nMax = static_cast<SCTAB>(maTabs.size());
5948     for (const auto& rTab : rMark)
5949     {
5950         if (rTab >= nMax)
5951             break;
5952         if (maTabs[rTab])
5953             maTabs[rTab]->DeleteSelection(nDelFlag, rMark, bBroadcast);
5954     }
5955 
5956     if (bDelContent)
5957     {
5958         // Re-start listeners on those top bottom groups that have been split.
5959         SetNeedsListeningGroups(aGroupPos);
5960         StartNeededListeners();
5961 
5962         // If formula groups were split their listeners were destroyed and may
5963         // need to be notified now that they're restored,
5964         // ScTable::DeleteSelection() couldn't do that.
5965         if (!aGroupPos.empty())
5966         {
5967             ScRangeList aRangeList;
5968             rMark.FillRangeListWithMarks( &aRangeList, false);
5969             for (size_t i = 0; i < aRangeList.size(); ++i)
5970             {
5971                 SetDirty( aRangeList[i], true);
5972             }
5973             //Notify listeners on top and bottom of the group that has been split
5974             for (size_t i = 0; i < aGroupPos.size(); ++i) {
5975                 ScFormulaCell *pFormulaCell = GetFormulaCell(aGroupPos[i]);
5976                 if (pFormulaCell)
5977                     pFormulaCell->SetDirty(true);
5978             }
5979         }
5980     }
5981 }
5982 
5983 void ScDocument::DeleteSelectionTab(
5984     SCTAB nTab, InsertDeleteFlags nDelFlag, const ScMarkData& rMark )
5985 {
5986     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
5987     {
5988         sc::AutoCalcSwitch aACSwitch(*this, false);
5989 
5990         std::vector<ScAddress> aGroupPos;
5991         // Destroy and reconstruct listeners only if content is affected.
5992         bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
5993         if (bDelContent)
5994         {
5995             // Record the positions of top and/or bottom formula groups that
5996             // intersect the area borders.
5997             sc::EndListeningContext aCxt(*this);
5998             ScRangeList aRangeList;
5999             rMark.FillRangeListWithMarks( &aRangeList, false);
6000             for (size_t i = 0; i < aRangeList.size(); ++i)
6001             {
6002                 const ScRange & rRange = aRangeList[i];
6003                 if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
6004                 {
6005                     ScRange aRange( rRange);
6006                     aRange.aStart.SetTab( nTab);
6007                     aRange.aEnd.SetTab( nTab);
6008                     EndListeningIntersectedGroups( aCxt, aRange, &aGroupPos);
6009                 }
6010             }
6011             aCxt.purgeEmptyBroadcasters();
6012         }
6013 
6014         maTabs[nTab]->DeleteSelection(nDelFlag, rMark);
6015 
6016         if (bDelContent)
6017         {
6018             // Re-start listeners on those top bottom groups that have been split.
6019             SetNeedsListeningGroups(aGroupPos);
6020             StartNeededListeners();
6021 
6022             // If formula groups were split their listeners were destroyed and may
6023             // need to be notified now that they're restored,
6024             // ScTable::DeleteSelection() couldn't do that.
6025             if (!aGroupPos.empty())
6026             {
6027                 ScRangeList aRangeList;
6028                 rMark.FillRangeListWithMarks( &aRangeList, false);
6029                 for (size_t i = 0; i < aRangeList.size(); ++i)
6030                 {
6031                     const ScRange & rRange = aRangeList[i];
6032                     if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
6033                     {
6034                         ScRange aRange( rRange);
6035                         aRange.aStart.SetTab( nTab);
6036                         aRange.aEnd.SetTab( nTab);
6037                         SetDirty( aRange, true);
6038                     }
6039                 }
6040             }
6041         }
6042     }
6043     else
6044     {
6045         OSL_FAIL("wrong table");
6046     }
6047 }
6048 
6049 ScPatternAttr* ScDocument::GetDefPattern() const
6050 {
6051     return const_cast<ScPatternAttr*>(&mxPoolHelper->GetDocPool()->GetDefaultItem(ATTR_PATTERN));
6052 }
6053 
6054 ScDocumentPool* ScDocument::GetPool()
6055 {
6056     return mxPoolHelper->GetDocPool();
6057 }
6058 
6059 ScStyleSheetPool* ScDocument::GetStyleSheetPool() const
6060 {
6061     return mxPoolHelper->GetStylePool();
6062 }
6063 
6064 SCSIZE ScDocument::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
6065                             SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, ScDirection eDir )
6066 {
6067     PutInOrder(nStartCol, nEndCol);
6068     PutInOrder(nStartRow, nEndRow);
6069     PutInOrder(nStartTab, nEndTab);
6070     if (ValidTab(nStartTab) && nStartTab < static_cast<SCTAB>(maTabs.size()))
6071     {
6072         if (maTabs[nStartTab])
6073             return maTabs[nStartTab]->GetEmptyLinesInBlock(nStartCol, nStartRow, nEndCol, nEndRow, eDir);
6074         else
6075             return 0;
6076     }
6077     else
6078         return 0;
6079 }
6080 
6081 void ScDocument::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, ScMoveDirection eDirection ) const
6082 {
6083     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6084         maTabs[nTab]->FindAreaPos( rCol, rRow, eDirection );
6085 }
6086 
6087 void ScDocument::GetNextPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCCOL nMovX, SCROW nMovY,
6088                                 bool bMarked, bool bUnprotected, const ScMarkData& rMark ) const
6089 {
6090     OSL_ENSURE( !nMovX || !nMovY, "GetNextPos: only X or Y" );
6091 
6092     ScMarkData aCopyMark = rMark;
6093     aCopyMark.SetMarking(false);
6094     aCopyMark.MarkToMulti();
6095 
6096     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6097         maTabs[nTab]->GetNextPos( rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark );
6098 }
6099 
6100 //  Data operations
6101 
6102 void ScDocument::UpdStlShtPtrsFrmNms()
6103 {
6104     ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
6105 
6106     for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
6107     {
6108         auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
6109         if (pPattern)
6110             pPattern->UpdateStyleSheet(this);
6111     }
6112     const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).UpdateStyleSheet(this);
6113 }
6114 
6115 void ScDocument::StylesToNames()
6116 {
6117     ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
6118 
6119     for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
6120     {
6121         auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
6122         if (pPattern)
6123             pPattern->StyleToName();
6124     }
6125     const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).StyleToName();
6126 }
6127 
6128 sal_uLong ScDocument::GetCellCount() const
6129 {
6130     sal_uLong nCellCount = 0;
6131 
6132     for (const auto& a : maTabs)
6133     {
6134         if (a)
6135             nCellCount += a->GetCellCount();
6136     }
6137 
6138     return nCellCount;
6139 }
6140 
6141 sal_uLong ScDocument::GetFormulaGroupCount() const
6142 {
6143     sal_uLong nFormulaGroupCount = 0;
6144 
6145     ScFormulaGroupIterator aIter( const_cast<ScDocument*>(this) );
6146     for ( sc::FormulaGroupEntry* ptr = aIter.first(); ptr; ptr = aIter.next())
6147     {
6148          nFormulaGroupCount++;
6149     }
6150 
6151     return nFormulaGroupCount;
6152 }
6153 
6154 sal_uLong ScDocument::GetCodeCount() const
6155 {
6156     sal_uLong nCodeCount = 0;
6157 
6158     for (const auto& a : maTabs)
6159     {
6160         if (a)
6161             nCodeCount += a->GetCodeCount();
6162     }
6163 
6164     return nCodeCount;
6165 }
6166 
6167 void ScDocument::PageStyleModified( SCTAB nTab, const OUString& rNewName )
6168 {
6169     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6170         maTabs[nTab]->PageStyleModified( rNewName );
6171 }
6172 
6173 void ScDocument::SetPageStyle( SCTAB nTab, const OUString& rName )
6174 {
6175     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6176         maTabs[nTab]->SetPageStyle( rName );
6177 }
6178 
6179 const OUString ScDocument::GetPageStyle( SCTAB nTab ) const
6180 {
6181     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6182         return maTabs[nTab]->GetPageStyle();
6183 
6184     return OUString();
6185 }
6186 
6187 void ScDocument::SetPageSize( SCTAB nTab, const Size& rSize )
6188 {
6189     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6190         maTabs[nTab]->SetPageSize( rSize );
6191 }
6192 
6193 Size ScDocument::GetPageSize( SCTAB nTab ) const
6194 {
6195     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6196         return maTabs[nTab]->GetPageSize();
6197 
6198     OSL_FAIL("invalid tab");
6199     return Size();
6200 }
6201 
6202 void ScDocument::SetRepeatArea( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow )
6203 {
6204     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6205         maTabs[nTab]->SetRepeatArea( nStartCol, nEndCol, nStartRow, nEndRow );
6206 }
6207 
6208 void ScDocument::InvalidatePageBreaks(SCTAB nTab)
6209 {
6210     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6211         maTabs[nTab]->InvalidatePageBreaks();
6212 }
6213 
6214 void ScDocument::UpdatePageBreaks( SCTAB nTab, const ScRange* pUserArea )
6215 {
6216     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6217         maTabs[nTab]->UpdatePageBreaks( pUserArea );
6218 }
6219 
6220 void ScDocument::RemoveManualBreaks( SCTAB nTab )
6221 {
6222     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6223         maTabs[nTab]->RemoveManualBreaks();
6224 }
6225 
6226 bool ScDocument::HasManualBreaks( SCTAB nTab ) const
6227 {
6228     if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
6229         return maTabs[nTab]->HasManualBreaks();
6230 
6231     OSL_FAIL("invalid tab");
6232     return false;
6233 }
6234 
6235 void ScDocument::GetDocStat( ScDocStat& rDocStat )
6236 {
6237     rDocStat.nTableCount = GetTableCount();
6238     rDocStat.aDocName    = aDocName;
6239     rDocStat.nFormulaCount = GetFormulaGroupCount();
6240     rDocStat.nCellCount  = GetCellCount();
6241 }
6242 
6243 bool ScDocument::HasPrintRange()
6244 {
6245     bool bResult = false;
6246 
6247     for (const auto& a : maTabs)
6248     {
6249         if (!a)
6250             continue;
6251         bResult = a->IsPrintEntireSheet() || (a->GetPrintRangeCount() > 0);
6252         if (bResult)
6253             break;
6254     }
6255 
6256     return bResult;
6257 }
6258 
6259 bool ScDocument::IsPrintEntireSheet( SCTAB nTab ) const
6260 {
6261     return (ValidTab(nTab) ) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsPrintEntireSheet();
6262 }
6263 
6264 sal_uInt16 ScDocument::GetPrintRangeCount( SCTAB nTab )
6265 {
6266     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6267         return maTabs[nTab]->GetPrintRangeCount();
6268 
6269     return 0;
6270 }
6271 
6272 const ScRange* ScDocument::GetPrintRange( SCTAB nTab, sal_uInt16 nPos )
6273 {
6274     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6275         return maTabs[nTab]->GetPrintRange(nPos);
6276 
6277     return nullptr;
6278 }
6279 
6280 const ScRange* ScDocument::GetRepeatColRange( SCTAB nTab )
6281 {
6282     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6283         return maTabs[nTab]->GetRepeatColRange();
6284 
6285     return nullptr;
6286 }
6287 
6288 const ScRange* ScDocument::GetRepeatRowRange( SCTAB nTab )
6289 {
6290     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6291         return maTabs[nTab]->GetRepeatRowRange();
6292 
6293     return nullptr;
6294 }
6295 
6296 void ScDocument::ClearPrintRanges( SCTAB nTab )
6297 {
6298     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6299         maTabs[nTab]->ClearPrintRanges();
6300 }
6301 
6302 void ScDocument::AddPrintRange( SCTAB nTab, const ScRange& rNew )
6303 {
6304     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6305         maTabs[nTab]->AddPrintRange( rNew );
6306 }
6307 
6308 void ScDocument::SetPrintEntireSheet( SCTAB nTab )
6309 {
6310     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6311         maTabs[nTab]->SetPrintEntireSheet();
6312 }
6313 
6314 void ScDocument::SetRepeatColRange( SCTAB nTab, std::unique_ptr<ScRange> pNew )
6315 {
6316     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6317         maTabs[nTab]->SetRepeatColRange( std::move(pNew) );
6318 }
6319 
6320 void ScDocument::SetRepeatRowRange( SCTAB nTab, std::unique_ptr<ScRange> pNew )
6321 {
6322     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6323         maTabs[nTab]->SetRepeatRowRange( std::move(pNew) );
6324 }
6325 
6326 std::unique_ptr<ScPrintRangeSaver> ScDocument::CreatePrintRangeSaver() const
6327 {
6328     const SCTAB nCount = static_cast<SCTAB>(maTabs.size());
6329     std::unique_ptr<ScPrintRangeSaver> pNew(new ScPrintRangeSaver( nCount ));
6330     for (SCTAB i=0; i<nCount; i++)
6331         if (maTabs[i])
6332             maTabs[i]->FillPrintSaver( pNew->GetTabData(i) );
6333     return pNew;
6334 }
6335 
6336 void ScDocument::RestorePrintRanges( const ScPrintRangeSaver& rSaver )
6337 {
6338     const SCTAB nCount = rSaver.GetTabCount();
6339     const SCTAB maxIndex = std::min(nCount, static_cast<SCTAB>(maTabs.size()));
6340     for (SCTAB i=0; i<maxIndex; i++)
6341         if (maTabs[i])
6342             maTabs[i]->RestorePrintRanges( rSaver.GetTabData(i) );
6343 }
6344 
6345 bool ScDocument::NeedPageResetAfterTab( SCTAB nTab ) const
6346 {
6347     // The page number count restarts at a sheet, if another template is set at
6348     // the preceding one (only compare names) and if a pagenumber is specified (not 0)
6349 
6350     if ( nTab + 1 < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab+1] )
6351     {
6352         const OUString & rNew = maTabs[nTab+1]->GetPageStyle();
6353         if ( rNew != maTabs[nTab]->GetPageStyle() )
6354         {
6355             SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( rNew, SfxStyleFamily::Page );
6356             if ( pStyle )
6357             {
6358                 const SfxItemSet& rSet = pStyle->GetItemSet();
6359                 sal_uInt16 nFirst = rSet.Get(ATTR_PAGE_FIRSTPAGENO).GetValue();
6360                 if ( nFirst != 0 )
6361                     return true;        // Specify page number in new template
6362             }
6363         }
6364     }
6365 
6366     return false;       // otherwise not
6367 }
6368 
6369 SfxUndoManager* ScDocument::GetUndoManager()
6370 {
6371     if (!mpUndoManager)
6372     {
6373         // to support enhanced text edit for draw objects, use an SdrUndoManager
6374         ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE);
6375 
6376         SdrUndoManager* pUndoManager = new SdrUndoManager;
6377         pUndoManager->SetDocShell(GetDocumentShell());
6378         mpUndoManager = pUndoManager;
6379     }
6380 
6381     return mpUndoManager;
6382 }
6383 
6384 ScRowBreakIterator* ScDocument::GetRowBreakIterator(SCTAB nTab) const
6385 {
6386     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6387         return new ScRowBreakIterator(maTabs[nTab]->maRowPageBreaks);
6388     return nullptr;
6389 }
6390 
6391 void ScDocument::AddSubTotalCell(ScFormulaCell* pCell)
6392 {
6393     maSubTotalCells.insert(pCell);
6394 }
6395 
6396 void ScDocument::RemoveSubTotalCell(ScFormulaCell* pCell)
6397 {
6398     maSubTotalCells.erase(pCell);
6399 }
6400 
6401 namespace {
6402 
6403 bool lcl_hasDirtyRange(ScFormulaCell* pCell, const ScRange& rDirtyRange)
6404 {
6405     ScDetectiveRefIter aRefIter(pCell);
6406     ScRange aRange;
6407     while (aRefIter.GetNextRef(aRange))
6408     {
6409         if (aRange.Intersects(rDirtyRange))
6410             return true;
6411     }
6412     return false;
6413 }
6414 
6415 }
6416 
6417 void ScDocument::SetSubTotalCellsDirty(const ScRange& rDirtyRange)
6418 {
6419     // to update the list by skipping cells that no longer contain subtotal function.
6420     set<ScFormulaCell*> aNewSet;
6421 
6422     bool bOldRecalc = GetAutoCalc();
6423     SetAutoCalc(false);
6424     for (ScFormulaCell* pCell : maSubTotalCells)
6425     {
6426         if (pCell->IsSubTotal())
6427         {
6428             aNewSet.insert(pCell);
6429             if (lcl_hasDirtyRange(pCell, rDirtyRange))
6430                 pCell->SetDirty();
6431         }
6432     }
6433 
6434     SetAutoCalc(bOldRecalc);
6435     maSubTotalCells.swap(aNewSet); // update the list.
6436 }
6437 
6438 sal_uInt16 ScDocument::GetTextWidth( const ScAddress& rPos ) const
6439 {
6440     SCTAB nTab = rPos.Tab();
6441     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6442         return maTabs[nTab]->GetTextWidth(rPos.Col(), rPos.Row());
6443 
6444     return 0;
6445 }
6446 
6447 SvtScriptType ScDocument::GetScriptType( const ScAddress& rPos ) const
6448 {
6449     SCTAB nTab = rPos.Tab();
6450     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6451         return maTabs[nTab]->GetScriptType(rPos.Col(), rPos.Row());
6452 
6453     return SvtScriptType::NONE;
6454 }
6455 
6456 void ScDocument::SetScriptType( const ScAddress& rPos, SvtScriptType nType )
6457 {
6458     SCTAB nTab = rPos.Tab();
6459     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
6460         maTabs[nTab]->SetScriptType(rPos.Col(), rPos.Row(), nType);
6461 }
6462 
6463 void ScDocument::EnableUndo( bool bVal )
6464 {
6465     // The undo manager increases lock count every time undo is disabled.
6466     // Because of this, we shouldn't disable undo unless it's currently
6467     // enabled, or else re-enabling it may not actually re-enable undo unless
6468     // the lock count becomes zero.
6469 
6470     if (bVal != GetUndoManager()->IsUndoEnabled())
6471     {
6472         GetUndoManager()->EnableUndo(bVal);
6473         if( mpDrawLayer ) mpDrawLayer->EnableUndo(bVal);
6474     }
6475 
6476     mbUndoEnabled = bVal;
6477 }
6478 
6479 void ScDocument::EnableUserInteraction( bool bVal )
6480 {
6481     mbUserInteractionEnabled = bVal;
6482 }
6483 
6484 bool ScDocument::IsInVBAMode() const
6485 {
6486     if (!mpShell)
6487         return false;
6488 
6489     try
6490     {
6491         uno::Reference<script::vba::XVBACompatibility> xVBA(
6492             mpShell->GetBasicContainer(), uno::UNO_QUERY);
6493 
6494         return xVBA.is() && xVBA->getVBACompatibilityMode();
6495     }
6496     catch (const lang::NotInitializedException&) {}
6497 
6498     return false;
6499 }
6500 
6501 ScPostIt* ScDocument::GetNote(const ScAddress& rPos)
6502 {
6503     return GetNote(rPos.Col(), rPos.Row(), rPos.Tab());
6504 }
6505 
6506 ScPostIt* ScDocument::GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab)
6507 {
6508     if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) &&
6509         nCol < maTabs[nTab]->GetAllocatedColumnsCount())
6510         return maTabs[nTab]->aCol[nCol].GetCellNote(nRow);
6511     else
6512         return nullptr;
6513 
6514 }
6515 
6516 void ScDocument::SetNote(const ScAddress& rPos, std::unique_ptr<ScPostIt> pNote)
6517 {
6518     return SetNote(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pNote));
6519 }
6520 
6521 void ScDocument::SetNote(SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPostIt> pNote)
6522 {
6523     return maTabs[nTab]->CreateColumnIfNotExists(nCol).SetCellNote(nRow, std::move(pNote));
6524 }
6525 
6526 bool ScDocument::HasNote(const ScAddress& rPos) const
6527 {
6528     return HasNote(rPos.Col(), rPos.Row(), rPos.Tab());
6529 }
6530 
6531 bool ScDocument::HasNote(SCCOL nCol, SCROW nRow, SCTAB nTab) const
6532 {
6533     if (!ValidColRow(nCol, nRow))
6534         return false;
6535 
6536     const ScTable* pTab = FetchTable(nTab);
6537     if (!pTab)
6538         return false;
6539 
6540     if (nCol >= pTab->GetAllocatedColumnsCount())
6541         return false;
6542 
6543     const ScPostIt* pNote = pTab->aCol[nCol].GetCellNote(nRow);
6544     return pNote != nullptr;
6545 }
6546 
6547 bool ScDocument::HasColNotes(SCCOL nCol, SCTAB nTab) const
6548 {
6549     if (!ValidCol(nCol))
6550         return false;
6551 
6552     const ScTable* pTab = FetchTable(nTab);
6553     if (!pTab)
6554         return false;
6555 
6556     return pTab->aCol[nCol].HasCellNotes();
6557 }
6558 
6559 bool ScDocument::HasTabNotes(SCTAB nTab) const
6560 {
6561     const ScTable* pTab = FetchTable(nTab);
6562 
6563     if ( !pTab )
6564         return false;
6565 
6566     for (SCCOL nCol=0, nColSize = pTab->aCol.size(); nCol < nColSize; ++nCol)
6567         if ( HasColNotes(nCol, nTab) )
6568             return true;
6569 
6570     return false;
6571 }
6572 
6573 bool ScDocument::HasNotes() const
6574 {
6575     for (SCTAB i = 0; i <= MAXTAB; ++i)
6576     {
6577         if (HasTabNotes(i))
6578             return true;
6579     }
6580     return false;
6581 }
6582 
6583 std::unique_ptr<ScPostIt> ScDocument::ReleaseNote(const ScAddress& rPos)
6584 {
6585     ScTable* pTab = FetchTable(rPos.Tab());
6586     if (!pTab)
6587         return nullptr;
6588 
6589     return pTab->ReleaseNote(rPos.Col(), rPos.Row());
6590 }
6591 
6592 ScPostIt* ScDocument::GetOrCreateNote(const ScAddress& rPos)
6593 {
6594     if (HasNote(rPos))
6595         return GetNote(rPos);
6596     else
6597         return CreateNote(rPos);
6598 }
6599 ScPostIt* ScDocument::CreateNote(const ScAddress& rPos)
6600 {
6601     ScPostIt* pPostIt = new ScPostIt(*this, rPos);
6602     SetNote(rPos, std::unique_ptr<ScPostIt>(pPostIt));
6603     return pPostIt;
6604 }
6605 
6606 size_t ScDocument::GetNoteCount( SCTAB nTab, SCCOL nCol ) const
6607 {
6608     const ScTable* pTab = FetchTable(nTab);
6609     if (!pTab)
6610         return 0;
6611 
6612     return pTab->GetNoteCount(nCol);
6613 }
6614 
6615 void ScDocument::CreateAllNoteCaptions()
6616 {
6617     for (const auto& a : maTabs)
6618     {
6619         if (a)
6620             a->CreateAllNoteCaptions();
6621     }
6622 }
6623 
6624 void ScDocument::ForgetNoteCaptions( const ScRangeList& rRanges, bool bPreserveData )
6625 {
6626     for (size_t i = 0, n = rRanges.size(); i < n; ++i)
6627     {
6628         const ScRange & rRange = rRanges[i];
6629         const ScAddress& s = rRange.aStart;
6630         const ScAddress& e = rRange.aEnd;
6631         for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab)
6632         {
6633             ScTable* pTab = FetchTable(nTab);
6634             if (!pTab)
6635                 continue;
6636 
6637             pTab->ForgetNoteCaptions(s.Col(), s.Row(), e.Col(), e.Row(), bPreserveData);
6638         }
6639     }
6640 }
6641 
6642 CommentCaptionState ScDocument::GetAllNoteCaptionsState( const ScRangeList& rRanges )
6643 {
6644     CommentCaptionState aTmpState = CommentCaptionState::ALLHIDDEN;
6645     CommentCaptionState aState = CommentCaptionState::ALLHIDDEN;
6646     bool bFirstControl = true;
6647     std::vector<sc::NoteEntry> aNotes;
6648 
6649     for (size_t i = 0, n = rRanges.size(); i < n; ++i)
6650     {
6651         const ScRange & rRange = rRanges[i];
6652 
6653         for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
6654         {
6655             aState = maTabs[nTab]->GetAllNoteCaptionsState( rRange, aNotes );
6656 
6657             if (aState == CommentCaptionState::MIXED)
6658                 return aState;
6659 
6660             if (bFirstControl)                      // it is possible that a range is ALLSHOWN, another range is ALLHIDDEN,
6661             {                                       // we have to detect that situation as mixed.
6662                 aTmpState = aState;
6663                 bFirstControl = false;
6664             }
6665             else if(aTmpState != aState)
6666             {
6667                 aState = CommentCaptionState::MIXED;
6668                 return aState;
6669             }
6670         }
6671     }
6672     return aState;
6673 }
6674 
6675 ScAddress ScDocument::GetNotePosition( size_t nIndex ) const
6676 {
6677     for (size_t nTab = 0; nTab < maTabs.size(); ++nTab)
6678     {
6679         for (SCCOL nCol : GetColumnsRange(nTab, 0, MAXCOL))
6680         {
6681             size_t nColNoteCount = GetNoteCount(nTab, nCol);
6682             if (!nColNoteCount)
6683                 continue;
6684 
6685             if (nIndex >= nColNoteCount)
6686             {
6687                 nIndex -= nColNoteCount;
6688                 continue;
6689             }
6690 
6691             SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
6692             if (nRow >= 0)
6693                 return ScAddress(nCol, nRow, nTab);
6694 
6695             OSL_FAIL("note not found");
6696             return ScAddress::INITIALIZE_INVALID;
6697         }
6698     }
6699 
6700     OSL_FAIL("note not found");
6701     return ScAddress::INITIALIZE_INVALID;
6702 }
6703 
6704 ScAddress ScDocument::GetNotePosition( size_t nIndex, SCTAB nTab ) const
6705 {
6706     for (SCCOL nCol : GetColumnsRange(nTab, 0, MAXCOL))
6707     {
6708         size_t nColNoteCount = GetNoteCount(nTab, nCol);
6709         if (!nColNoteCount)
6710             continue;
6711 
6712         if (nIndex >= nColNoteCount)
6713         {
6714             nIndex -= nColNoteCount;
6715             continue;
6716         }
6717 
6718         SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
6719         if (nRow >= 0)
6720             return ScAddress(nCol, nRow, nTab);
6721 
6722         OSL_FAIL("note not found");
6723         return ScAddress::INITIALIZE_INVALID;
6724     }
6725 
6726     OSL_FAIL("note not found");
6727     return ScAddress::INITIALIZE_INVALID;
6728 }
6729 
6730 SCROW ScDocument::GetNotePosition( SCTAB nTab, SCCOL nCol, size_t nIndex ) const
6731 {
6732     const ScTable* pTab = FetchTable(nTab);
6733     if (!pTab)
6734         return -1;
6735 
6736     return pTab->GetNotePosition(nCol, nIndex);
6737 }
6738 
6739 void ScDocument::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
6740 {
6741     for (const auto & pTab : maTabs)
6742     {
6743         if (!pTab)
6744             continue;
6745 
6746         pTab->GetAllNoteEntries(rNotes);
6747     }
6748 }
6749 
6750 void ScDocument::GetAllNoteEntries( SCTAB nTab, std::vector<sc::NoteEntry>& rNotes ) const
6751 {
6752     const ScTable* pTab = FetchTable(nTab);
6753     if (!pTab)
6754         return;
6755 
6756     return pTab->GetAllNoteEntries( rNotes );
6757 }
6758 
6759 void ScDocument::GetNotesInRange( const ScRangeList& rRangeList, std::vector<sc::NoteEntry>& rNotes ) const
6760 {
6761     for( size_t i = 0; i < rRangeList.size(); ++i)
6762     {
6763         const ScRange & rRange = rRangeList[i];
6764         for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
6765         {
6766             maTabs[nTab]->GetNotesInRange( rRange, rNotes );
6767         }
6768     }
6769 }
6770 
6771 void ScDocument::GetUnprotectedCells( ScRangeList& rRangeList, SCTAB nTab ) const
6772 {
6773     maTabs[nTab]->GetUnprotectedCells( rRangeList );
6774 }
6775 
6776 bool ScDocument::ContainsNotesInRange( const ScRangeList& rRangeList ) const
6777 {
6778     for( size_t i = 0; i < rRangeList.size(); ++i)
6779     {
6780         const ScRange & rRange = rRangeList[i];
6781         for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
6782         {
6783             bool bContainsNote = maTabs[nTab]->ContainsNotesInRange( rRange );
6784             if(bContainsNote)
6785                 return true;
6786         }
6787     }
6788 
6789     return false;
6790 }
6791 
6792 void ScDocument::SetAutoNameCache( std::unique_ptr<ScAutoNameCache> pCache )
6793 {
6794     pAutoNameCache = std::move(pCache);
6795 }
6796 
6797 thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific;
6798 
6799 ScRecursionHelper& ScDocument::GetRecursionHelper()
6800 {
6801     if (!IsThreadedGroupCalcInProgress())
6802     {
6803         if (!maNonThreaded.pRecursionHelper)
6804             maNonThreaded.pRecursionHelper = CreateRecursionHelperInstance();
6805         return *maNonThreaded.pRecursionHelper;
6806     }
6807     else
6808     {
6809         if (!maThreadSpecific.pRecursionHelper)
6810             maThreadSpecific.pRecursionHelper = CreateRecursionHelperInstance();
6811         return *maThreadSpecific.pRecursionHelper;
6812     }
6813 }
6814 
6815 void ScDocumentThreadSpecific::SetupFromNonThreadedData(const ScDocumentThreadSpecific& /*rNonThreadedData*/)
6816 {
6817     // What about the recursion helper?
6818     // Copy the lookup cache?
6819 }
6820 
6821 void ScDocumentThreadSpecific::MergeBackIntoNonThreadedData(ScDocumentThreadSpecific& /*rNonThreadedData*/)
6822 {
6823     // What about recursion helper and lookup cache?
6824 }
6825 
6826 void ScDocument::SetupFromNonThreadedContext(ScInterpreterContext& /*threadedContext*/, int /*threadNumber*/)
6827 {
6828     // lookup cache is now only in pooled ScInterpreterContext's
6829 }
6830 
6831 void ScDocument::MergeBackIntoNonThreadedContext(ScInterpreterContext& threadedContext, int /*threadNumber*/)
6832 {
6833     // Move data from a context used by a calculation thread to the main thread's context.
6834     // Called from the main thread after the calculation thread has already finished.
6835     assert(!IsThreadedGroupCalcInProgress());
6836     maInterpreterContext.maDelayedSetNumberFormat.insert(
6837         maInterpreterContext.maDelayedSetNumberFormat.end(),
6838         std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.begin()),
6839         std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.end()));
6840     // lookup cache is now only in pooled ScInterpreterContext's
6841 }
6842 
6843 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
6844