LibreOffice Module sc (master) 1
document.cxx
Go to the documentation of this file.
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 <o3tl/safeint.hxx>
26#include <svx/svditer.hxx>
27#include <sfx2/objsh.hxx>
28#include <sfx2/docfile.hxx>
29#include <svl/numformat.hxx>
30#include <svl/poolcach.hxx>
31#include <svl/zforlist.hxx>
34#include <tools/urlobj.hxx>
35#include <sal/log.hxx>
36#include <osl/diagnose.h>
37
38#include <com/sun/star/text/WritingMode2.hpp>
39#include <com/sun/star/script/vba/XVBACompatibility.hpp>
40#include <com/sun/star/sheet/TablePageBreakData.hpp>
41#include <com/sun/star/lang/NotInitializedException.hpp>
42
43#include <document.hxx>
44#include <docsh.hxx>
45#include <docuno.hxx>
46#include <table.hxx>
47#include <column.hxx>
48#include <attrib.hxx>
49#include <attarray.hxx>
50#include <patattr.hxx>
51#include <rangenam.hxx>
52#include <poolhelp.hxx>
53#include <docpool.hxx>
54#include <stlpool.hxx>
55#include <stlsheet.hxx>
56#include <globstr.hrc>
57#include <scresid.hxx>
58#include <dbdata.hxx>
59#include <chartlis.hxx>
60#include <rangelst.hxx>
61#include <markdata.hxx>
62#include <drwlayer.hxx>
63#include <validat.hxx>
64#include <prnsave.hxx>
65#include <chgtrack.hxx>
66#include <hints.hxx>
67#include <detdata.hxx>
68#include <dpobject.hxx>
69#include <scmod.hxx>
70#include <dociter.hxx>
71#include <progress.hxx>
72#include <autonamecache.hxx>
73#include <bcaslot.hxx>
74#include <postit.hxx>
75#include <clipparam.hxx>
76#include <defaultsoptions.hxx>
77#include <editutil.hxx>
78#include <stringutil.hxx>
79#include <formulaiter.hxx>
80#include <formulacell.hxx>
81#include <clipcontext.hxx>
82#include <listenercontext.hxx>
83#include <scopetools.hxx>
84#include <refupdatecontext.hxx>
85#include <formulagroup.hxx>
87#include <compressedarray.hxx>
88#include <recursionhelper.hxx>
89#include <SparklineGroup.hxx>
90#include <SparklineList.hxx>
91#include <undomanager.hxx>
92
94
95#include <limits>
96#include <memory>
97#include <utility>
98
99#include <comphelper/lok.hxx>
101
102#include <vcl/uitest/logger.hxx>
104
105#include <mtvelements.hxx>
106#include <sfx2/lokhelper.hxx>
107
108using ::editeng::SvxBorderLine;
109using namespace ::com::sun::star;
110
111namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
112using ::com::sun::star::uno::Sequence;
113using ::com::sun::star::sheet::TablePageBreakData;
114using ::std::set;
115
116namespace {
117
118std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
119{
120 SCTAB nTabStart = MAXTAB;
121 SCTAB nTabEnd = 0;
122 SCTAB nMax = static_cast<SCTAB>(rTables.size());
123 for (const auto& rTab : rMark)
124 {
125 if (rTab >= nMax)
126 break;
127
128 if (!rTables[rTab])
129 continue;
130
131 if (rTab < nTabStart)
132 nTabStart = rTab;
133 nTabEnd = rTab;
134 }
135
136 return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
137}
138
139void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
140{
141 EventDescription aDescription;
142 aDescription.aID = "grid_window";
143 aDescription.aAction = rAction;
144 aDescription.aParameters = std::move(aParameters);
145 aDescription.aParent = "MainWindow";
146 aDescription.aKeyWord = "ScGridWinUIObject";
147
148 UITestLogger::getInstance().logEvent(aDescription);
149}
150
151struct ScDefaultAttr
152{
153 const ScPatternAttr* pAttr;
154 SCROW nFirst;
156 explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {}
157};
158
159struct ScLessDefaultAttr
160{
161 bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const
162 {
163 return rValue1.pAttr < rValue2.pAttr;
164 }
165};
166
167}
168
169typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet;
170
171void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
172{
173 if (!ValidTab(nTab) || HasTable(nTab))
174 return;
175
176 // Get Custom prefix
177 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
178 OUString aString = rOpt.GetInitTabPrefix() + OUString::number(nTab+1);
179 if ( _bNeedsNameCheck )
180 CreateValidTabName( aString ); // no doubles
181 if (nTab < GetTableCount())
182 {
183 maTabs[nTab].reset( new ScTable(*this, nTab, aString) );
184 }
185 else
186 {
187 while (nTab > GetTableCount())
188 maTabs.push_back(nullptr);
189 maTabs.emplace_back( new ScTable(*this, nTab, aString) );
190 }
191 maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
192}
193
194bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
195{
196 if (const ScTable* pTable = FetchTable(nTab))
197 {
198 rHashCode = pTable->GetHashCode();
199 return true;
200 }
201 return false;
202}
203
204bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
205{
206 if (const ScTable* pTable = FetchTable(nTab))
207 {
208 rName = pTable->GetName();
209 return true;
210 }
211 rName.clear();
212 return false;
213}
214
215OUString ScDocument::GetCopyTabName( SCTAB nTab ) const
216{
217 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
218 return maTabNames[nTab];
219 return OUString();
220}
221
222bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
223{
224 if (ScTable* pTable = FetchTable(nTab))
225 {
226 pTable->SetCodeName(rName);
227 return true;
228 }
229 SAL_WARN("sc", "can't set code name " << rName );
230 return false;
231}
232
233bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
234{
235 if (const ScTable* pTable = FetchTable(nTab))
236 {
237 rName = pTable->GetCodeName();
238 return true;
239 }
240 rName.clear();
241 return false;
242}
243
244bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
245{
246 static OUString aCacheName, aCacheUpperName;
247
249 if (aCacheName != rName)
250 {
251 aCacheName = rName;
252 // surprisingly slow ...
253 aCacheUpperName = ScGlobal::getCharClass().uppercase(rName);
254 }
255 const OUString aUpperName = aCacheUpperName;
256
257 for (SCTAB i = 0; i < GetTableCount(); i++)
258 if (maTabs[i])
259 {
260 if (aUpperName == maTabs[i]->GetUpperName())
261 {
262 rTab = i;
263 return true;
264 }
265 }
266 rTab = 0;
267 return false;
268}
269
270std::vector<OUString> ScDocument::GetAllTableNames() const
271{
272 std::vector<OUString> aNames;
273 aNames.reserve(maTabs.size());
274 for (const auto& a : maTabs)
275 {
276 // Positions need to be preserved for ScCompiler and address convention
277 // context, so still push an empty string for NULL tabs.
278 OUString aName;
279 if (a)
280 {
281 const ScTable& rTab = *a;
282 aName = rTab.GetName();
283 }
284 aNames.push_back(aName);
285 }
286
287 return aNames;
288}
289
291{
292 if (ScTable* pTable = FetchTable(nTab))
293 return pTable->GetAnonymousDBData();
294 return nullptr;
295}
296
298{
299 return static_cast<SCTAB>(maTabs.size());
300}
301
302void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
303{
304 if (ScTable* pTable = FetchTable(nTab))
305 pTable->SetAnonymousDBData(std::move(pDBData));
306}
307
308void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
309{
310 mpAnonymousDBData = std::move(pDBData);
311}
312
314{
315 return mpAnonymousDBData.get();
316}
317
318bool ScDocument::ValidTabName( const OUString& rName )
319{
320 if (rName.isEmpty())
321 return false;
322 sal_Int32 nLen = rName.getLength();
323
324#if 1
325 // Restrict sheet names to what Excel accepts.
326 /* TODO: We may want to remove this restriction for full ODFF compliance.
327 * Merely loading and calculating ODF documents using these characters in
328 * sheet names is not affected by this, but all sheet name editing and
329 * copying functionality is, maybe falling back to "Sheet4" or similar. */
330 for (sal_Int32 i = 0; i < nLen; ++i)
331 {
332 const sal_Unicode c = rName[i];
333 switch (c)
334 {
335 case ':':
336 case '\\':
337 case '/':
338 case '?':
339 case '*':
340 case '[':
341 case ']':
342 // these characters are not allowed to match XL's convention.
343 return false;
344 case '\'':
345 if (i == 0 || i == nLen - 1)
346 // single quote is not allowed at the first or last
347 // character position.
348 return false;
349 break;
350 }
351 }
352#endif
353
354 return true;
355}
356
357bool ScDocument::ValidNewTabName( const OUString& rName ) const
358{
359 bool bValid = ValidTabName(rName);
360 if (!bValid)
361 return false;
362 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
363 for (const auto& a : maTabs)
364 {
365 if (!a)
366 continue;
367 const OUString& rOldName = a->GetUpperName();
368 bValid = rOldName != aUpperName;
369 if (!bValid)
370 break;
371 }
372 return bValid;
373}
374
375void ScDocument::CreateValidTabName(OUString& rName) const
376{
377 if ( !ValidTabName(rName) )
378 {
379 // Find new one
380
381 // Get Custom prefix
382 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
383 const OUString& aStrTable = rOpt.GetInitTabPrefix();
384
385 bool bOk = false;
386
387 // First test if the prefix is valid, if so only avoid doubles
388 bool bPrefix = ValidTabName( aStrTable );
389 OSL_ENSURE(bPrefix, "Invalid Table Name");
390 SCTAB nDummy;
391
392 for (SCTAB i = GetTableCount() + 1; !bOk ; i++)
393 {
394 rName = aStrTable + OUString::number(static_cast<sal_Int32>(i));
395 if (bPrefix)
396 bOk = ValidNewTabName( rName );
397 else
398 bOk = !GetTable( rName, nDummy );
399 }
400 }
401 else
402 {
403 // testing the supplied Name
404
405 if ( !ValidNewTabName(rName) )
406 {
407 SCTAB i = 1;
408 OUString aName;
409 do
410 {
411 i++;
412 aName = rName + "_" + OUString::number(static_cast<sal_Int32>(i));
413 }
414 while (!ValidNewTabName(aName) && (i < MAXTAB+1));
415 rName = aName;
416 }
417 }
418}
419
420void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
421{
422 aNames.clear();//ensure that the vector is empty
423
424 // Get Custom prefix
425 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
426 const OUString& aStrTable = rOpt.GetInitTabPrefix();
427
428 OUStringBuffer rName;
429
430 // First test if the prefix is valid, if so only avoid doubles
431 bool bPrefix = ValidTabName( aStrTable );
432 OSL_ENSURE(bPrefix, "Invalid Table Name");
433 SCTAB nDummy;
434 SCTAB i = GetTableCount() + 1;
435
436 for (SCTAB j = 0; j < nCount; ++j)
437 {
438 bool bOk = false;
439 while(!bOk)
440 {
441 rName = aStrTable;
442 rName.append(static_cast<sal_Int32>(i));
443 if (bPrefix)
444 bOk = ValidNewTabName( rName.toString() );
445 else
446 bOk = !GetTable( rName.toString(), nDummy );
447 i++;
448 }
449 aNames.push_back(rName.makeStringAndClear());
450 }
451}
452
453void ScDocument::AppendTabOnLoad(const OUString& rName)
454{
455 SCTAB nTabCount = GetTableCount();
456 if (!ValidTab(nTabCount))
457 // max table count reached. No more tables.
458 return;
459
460 OUString aName = rName;
462 maTabs.emplace_back( new ScTable(*this, nTabCount, aName) );
463}
464
465void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
466{
467 if (!ValidTab(nTab) || GetTableCount() <= nTab)
468 return;
469
470 if (!ValidTabName(rName))
471 return;
472
473 maTabs[nTab]->SetName(rName);
474}
475
477{
478 for (const auto& a : maTabs)
479 {
480 if (a)
481 a->SetStreamValid(false);
482 }
483}
484
486 SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
487{
488 SCTAB nTabCount = GetTableCount();
489 bool bValid = ValidTab(nTabCount);
490 if ( !bExternalDocument ) // else test rName == "'Doc'!Tab" first
491 bValid = (bValid && ValidNewTabName(rName));
492 if (bValid)
493 {
494 if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
495 {
496 nPos = maTabs.size();
497 maTabs.emplace_back( new ScTable(*this, nTabCount, rName) );
498 if ( bExternalDocument )
499 maTabs[nTabCount]->SetVisible( false );
500 }
501 else
502 {
503 if (ValidTab(nPos) && (nPos < nTabCount))
504 {
505 sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);
506
507 ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
508 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
509 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
510 if (pRangeName)
511 pRangeName->UpdateInsertTab(aCxt);
512 pDBCollection->UpdateReference(
513 URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
514 if (pDPCollection)
515 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
516 if (pDetOpList)
517 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
518 UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
519 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
520 if ( pUnoBroadcaster )
521 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
522
523 for (const auto& a : maTabs)
524 {
525 if (a)
526 a->UpdateInsertTab(aCxt);
527 }
528 maTabs.emplace(maTabs.begin() + nPos, new ScTable(*this, nPos, rName));
529
530 // UpdateBroadcastAreas must be called between UpdateInsertTab,
531 // which ends listening, and StartAllListeners, to not modify
532 // areas that are to be inserted by starting listeners.
533 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
534 for (const auto& a : maTabs)
535 {
536 if (a)
537 a->UpdateCompile();
538 }
539
541
542 if (pValidationList)
543 {
545 pValidationList->UpdateInsertTab(aCxt);
546 }
547
548 bValid = true;
549 }
550 else
551 bValid = false;
552 }
553 }
554
555 if (bValid)
556 {
558 aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
559 aCxt.mnTabDeletedStart = nPos;
560 aCxt.mnTabDeletedEnd = nPos;
562
564 {
565 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
567 }
568 }
569
570 return bValid;
571}
572
573bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
574 bool bNamesValid )
575{
576 SCTAB nNewSheets = static_cast<SCTAB>(rNames.size());
577 SCTAB nTabCount = GetTableCount();
578 bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);
579
580 if (bValid)
581 {
582 if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
583 {
584 for ( SCTAB i = 0; i < nNewSheets; ++i )
585 {
586 maTabs.emplace_back( new ScTable(*this, nTabCount + i, rNames.at(i)) );
587 }
588 }
589 else
590 {
591 if (ValidTab(nPos) && (nPos < nTabCount))
592 {
593 sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
594 ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
595 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
596 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
597 if (pRangeName)
598 pRangeName->UpdateInsertTab(aCxt);
599 pDBCollection->UpdateReference(
600 URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
601 if (pDPCollection)
602 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
603 if (pDetOpList)
604 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets );
605 UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
606 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
607 if ( pUnoBroadcaster )
608 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );
609
610 for (const auto& a : maTabs)
611 {
612 if (a)
613 a->UpdateInsertTab(aCxt);
614 }
615 for (SCTAB i = 0; i < nNewSheets; ++i)
616 {
617 maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(*this, nPos + i, rNames.at(i)) );
618 }
619
620 // UpdateBroadcastAreas must be called between UpdateInsertTab,
621 // which ends listening, and StartAllListeners, to not modify
622 // areas that are to be inserted by starting listeners.
623 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
624 for (const auto& a : maTabs)
625 {
626 if (a)
627 a->UpdateCompile();
628 }
629
631
632 if (pValidationList)
633 {
635 pValidationList->UpdateInsertTab(aCxt);
636 }
637
638 bValid = true;
639 }
640 else
641 bValid = false;
642 }
643 }
644
645 if (bValid)
646 {
649 }
650
651 return bValid;
652}
653
655{
656 bool bValid = false;
657 if (HasTable(nTab))
658 {
659 SCTAB nTabCount = GetTableCount();
660 if (nTabCount > 1)
661 {
662 sc::AutoCalcSwitch aACSwitch(*this, false);
663 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
664 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
665
666 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab );
667 DelBroadcastAreasInRange( aRange );
668
669 // #i8180# remove database ranges etc. that are on the deleted tab
670 // (restored in undo with ScRefUndoData)
671
672 xColNameRanges->DeleteOnTab( nTab );
673 xRowNameRanges->DeleteOnTab( nTab );
674 pDBCollection->DeleteOnTab( nTab );
675 if (pDPCollection)
676 pDPCollection->DeleteOnTab( nTab );
677 if (pDetOpList)
678 pDetOpList->DeleteOnTab( nTab );
679 DeleteAreaLinksOnTab( nTab );
680
681 // normal reference update
682
683 aRange.aEnd.SetTab(GetTableCount() - 1);
684 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
685 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
686 if (pRangeName)
687 pRangeName->UpdateDeleteTab(aCxt);
688 pDBCollection->UpdateReference(
689 URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
690 if (pDPCollection)
691 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
692 if (pDetOpList)
693 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 );
694 UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
695 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
696 if (pValidationList)
697 {
699 pValidationList->UpdateDeleteTab(aCxt);
700 }
701 if ( pUnoBroadcaster )
702 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );
703
704 for (auto & pTab : maTabs)
705 if (pTab)
706 pTab->UpdateDeleteTab(aCxt);
707
708 // tdf#149502 make sure ScTable destructor called after the erase is finished, when
709 // maTabs[x].nTab==x is true again, as it should be always true.
710 // In the end of maTabs.erase, maTabs indexes change, but nTab updated before erase.
711 // ~ScTable expect that maTabs[x].nTab==x so it shouldn't be called during erase.
712 ScTableUniquePtr pErasedTab = std::move(maTabs[nTab]);
713 maTabs.erase(maTabs.begin() + nTab);
714 delete pErasedTab.release();
715
716 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
717 // which ends listening, and StartAllListeners, to not modify
718 // areas that are to be inserted by starting listeners.
719 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
720 for (const auto& a : maTabs)
721 {
722 if (a)
723 a->UpdateCompile();
724 }
725 // Excel-Filter deletes some Tables while loading, Listeners will
726 // only be triggered after the loading is done.
728 {
730
731 sc::SetFormulaDirtyContext aFormulaDirtyCxt;
732 SetAllFormulasDirty(aFormulaDirtyCxt);
733 }
734
736 {
737 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
739 }
740
741 bValid = true;
742 }
743 }
744 return bValid;
745}
746
747bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
748{
749 bool bValid = false;
750 if (HasTable(nTab) && (nTab + nSheets) <= GetTableCount())
751 {
752 SCTAB nTabCount = GetTableCount();
753 if (nTabCount > nSheets)
754 {
755 sc::AutoCalcSwitch aACSwitch(*this, false);
756 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
757 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
758
759 for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
760 {
761 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab + aTab );
762 DelBroadcastAreasInRange( aRange );
763
764 // #i8180# remove database ranges etc. that are on the deleted tab
765 // (restored in undo with ScRefUndoData)
766
767 xColNameRanges->DeleteOnTab( nTab + aTab );
768 xRowNameRanges->DeleteOnTab( nTab + aTab );
769 pDBCollection->DeleteOnTab( nTab + aTab );
770 if (pDPCollection)
771 pDPCollection->DeleteOnTab( nTab + aTab );
772 if (pDetOpList)
773 pDetOpList->DeleteOnTab( nTab + aTab );
774 DeleteAreaLinksOnTab( nTab + aTab );
775 }
776
777 if (pRangeName)
778 pRangeName->UpdateDeleteTab(aCxt);
779
780 // normal reference update
781
782 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTabCount - 1 );
783 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
784 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
785 pDBCollection->UpdateReference(
786 URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
787 if (pDPCollection)
788 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
789 if (pDetOpList)
790 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets );
791 UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
792 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
793 if (pValidationList)
794 {
796 pValidationList->UpdateDeleteTab(aCxt);
797 }
798 if ( pUnoBroadcaster )
799 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );
800
801 for (auto & pTab : maTabs)
802 if (pTab)
803 pTab->UpdateDeleteTab(aCxt);
804
805 maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
806 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
807 // which ends listening, and StartAllListeners, to not modify
808 // areas that are to be inserted by starting listeners.
809 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
810 for (const auto& a : maTabs)
811 {
812 if (a)
813 a->UpdateCompile();
814 }
815 // Excel-Filter deletes some Tables while loading, Listeners will
816 // only be triggered after the loading is done.
818 {
820
821 sc::SetFormulaDirtyContext aFormulaDirtyCxt;
822 SetAllFormulasDirty(aFormulaDirtyCxt);
823 }
824
826 {
827 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
829 }
830
831 bValid = true;
832 }
833 }
834 return bValid;
835}
836
837bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
838{
839 bool bValid = false;
840 SCTAB i;
841 if (HasTable(nTab))
842 {
843 if ( bExternalDocument )
844 bValid = true; // composed name
845 else
846 bValid = ValidTabName(rName);
847 for (i = 0; i < GetTableCount() && bValid; i++)
848 {
849 if (maTabs[i] && (i != nTab))
850 {
851 OUString aOldName = maTabs[i]->GetName();
852 bValid = !ScGlobal::GetTransliteration().isEqual( rName, aOldName );
853 }
854 }
855 if (bValid)
856 {
857 // #i75258# update charts before renaming, so they can get their live data objects.
858 // Once the charts are live, the sheet can be renamed without problems.
860 pChartListenerCollection->UpdateChartsContainingTab( nTab );
861 maTabs[nTab]->SetName(rName);
862
863 // If formulas refer to the renamed sheet, the TokenArray remains valid,
864 // but the XML stream must be re-generated.
865 for (const auto& pTable : maTabs)
866 {
867 if (pTable)
868 pTable->SetStreamValid( false );
869 }
870
872 {
873 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
875 }
876 }
877 }
878
879 collectUIInformation({{"NewName", rName}}, "Rename_Sheet");
880
881 return bValid;
882}
883
884void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
885{
886 if (ScTable* pTable = FetchTable(nTab))
887 pTable->SetVisible(bVisible);
888}
889
890bool ScDocument::IsVisible( SCTAB nTab ) const
891{
892 if (const ScTable* pTable = FetchTable(nTab))
893 return pTable->IsVisible();
894 return false;
895}
896
898{
899 if (const ScTable* pTable = FetchTable(nTab))
900 return pTable->IsStreamValid();
901 return false;
902}
903
904void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
905{
906 if (ScTable* pTable = FetchTable(nTab))
907 pTable->SetStreamValid( bSet, bIgnoreLock );
908}
909
911{
912 mbStreamValidLocked = bLock;
913}
914
916{
917 if (const ScTable* pTable = FetchTable(nTab))
918 return pTable->IsPendingRowHeights();
919 return false;
920}
921
923{
924 if (ScTable* pTable = FetchTable(nTab))
925 pTable->SetPendingRowHeights(bSet);
926}
927
929{
930 const ScTable* pTab = FetchTable(nTab);
931 if (!pTab)
933
934 return pTab->GetOptimalMinRowHeight();
935}
936
937void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling)
938{
939 ScTable* pTable = FetchTable(nTab);
940 if (!pTable)
941 return;
942
943 if ( bImportingXML )
944 {
945 // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
946 // is applied in SetImportingXML(false). This is so the shapes can be loaded in
947 // normal LTR mode.
948
949 pTable->SetLoadingRTL( bRTL );
950 return;
951 }
952
953 pTable->SetLayoutRTL( bRTL ); // only sets the flag
954 pTable->SetDrawPageSize(true, true, eObjectHandling);
955
956 // objects are already repositioned via SetDrawPageSize, only writing mode is missing
957 if (!mpDrawLayer)
958 return;
959
960 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
961 OSL_ENSURE(pPage,"Page ?");
962 if (!pPage)
963 return;
964
965 SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
966 SdrObject* pObject = aIter.Next();
967 while (pObject)
968 {
969 pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
970 pObject = aIter.Next();
971 }
972}
973
975{
976 if (const ScTable* pTable = FetchTable(nTab))
977 return pTable->IsLayoutRTL();
978
979 return false;
980}
981
983{
984 // Negative page area is always used for RTL layout.
985 // The separate method is used to find all RTL handling of drawing objects.
986 return IsLayoutRTL( nTab );
987}
988
989/* ----------------------------------------------------------------------------
990 used search area:
991
992 GetCellArea - Only Data
993 GetTableArea - Data / Attributes
994 GetPrintArea - intended for character objects,
995 sweeps attributes all the way to bottom / right
996---------------------------------------------------------------------------- */
997
998bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
999{
1000 if (HasTable(nTab))
1001 return maTabs[nTab]->GetCellArea(rEndCol, rEndRow);
1002
1003 rEndCol = 0;
1004 rEndRow = 0;
1005 return false;
1006}
1007
1008bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
1009{
1010 if (const ScTable* pTable = FetchTable(nTab))
1011 return pTable->GetTableArea(rEndCol, rEndRow, bCalcHiddens);
1012
1013 rEndCol = 0;
1014 rEndRow = 0;
1015 return false;
1016}
1017
1018bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
1019{
1020 if (!HasTable(nTab))
1021 return false;
1022
1023 SCCOL nCol1, nCol2;
1024 SCROW nRow1, nRow2;
1025 maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
1026 maTabs[nTab]->GetLastDataPos(nCol2, nRow2);
1027
1028 if (nCol1 > nCol2 || nRow1 > nRow2)
1029 // invalid range.
1030 return false;
1031
1032 // Make sure the area only shrinks, and doesn't grow.
1033 if (rStartCol < nCol1)
1034 rStartCol = nCol1;
1035 if (nCol2 < rEndCol)
1036 rEndCol = nCol2;
1037 if (rStartRow < nRow1)
1038 rStartRow = nRow1;
1039 if (nRow2 < rEndRow)
1040 rEndRow = nRow2;
1041
1042 if (rStartCol > rEndCol || rStartRow > rEndRow)
1043 // invalid range.
1044 return false;
1045
1046 return true; // success!
1047}
1048
1049bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
1050 SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
1051 bool bStickyTopRow, bool bStickyLeftCol, ScDataAreaExtras* pDataAreaExtras ) const
1052{
1053 if (const ScTable* pTable = FetchTable(nTab))
1054 {
1055 return pTable->ShrinkToUsedDataArea(
1056 o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, bColumnsOnly, bStickyTopRow,
1057 bStickyLeftCol, pDataAreaExtras);
1058 }
1059 o_bShrunk = false;
1060 return false;
1061}
1062
1063SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
1064{
1065 if (const ScTable* pTable = FetchTable(nTab))
1066 return pTable->GetLastDataRow(nCol1, nCol2, nLastRow);
1067 return -1;
1068}
1069
1070// connected area
1071
1072void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1073 SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
1074{
1075 if (const ScTable* pTable = FetchTable(nTab))
1076 pTable->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
1077}
1078
1080{
1081 SCTAB nTab = rRange.aStart.Tab();
1082 if (nTab != rRange.aEnd.Tab())
1083 return true;
1084
1085 if (const ScTable* pTable = FetchTable(nTab))
1086 return pTable->GetDataAreaSubrange(rRange);
1087
1088 return true;
1089}
1090
1091void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1092 SCCOL& rEndCol, SCROW& rEndRow )
1093{
1094 if (ScTable* pTable = FetchTable(nTab))
1095 pTable->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow);
1096}
1097
1099{
1100 ScRangeListRef aNew = new ScRangeList;
1101 if (rRangeList.is())
1102 {
1103 for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
1104 {
1105 ScRange aRange( (*rRangeList)[i] );
1106 if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MaxCol() ) ||
1107 ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MaxRow() ) )
1108 {
1109 SCCOL nStartCol = aRange.aStart.Col();
1110 SCROW nStartRow = aRange.aStart.Row();
1111 SCCOL nEndCol = aRange.aEnd.Col();
1112 SCROW nEndRow = aRange.aEnd.Row();
1113 SCTAB nTab = aRange.aStart.Tab();
1114 if (ScTable* pTable = FetchTable(nTab))
1115 pTable->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
1116 aRange.aStart.SetCol( nStartCol );
1117 aRange.aStart.SetRow( nStartRow );
1118 aRange.aEnd.SetCol( nEndCol );
1119 aRange.aEnd.SetRow( nEndRow );
1120 }
1121 aNew->push_back(aRange);
1122 }
1123 }
1124 else
1125 {
1126 OSL_FAIL("LimitChartIfAll: Ref==0");
1127 }
1128 rRangeList = aNew;
1129}
1130
1131static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1132{
1133 // without ScMarkData, leave start/end unchanged
1134 if ( !pTabMark )
1135 return;
1136
1137 for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
1138 if (pTabMark->GetTableSelect(nTab))
1139 {
1140 // find first range of consecutive selected sheets
1141 rTabRangeStart = pTabMark->GetFirstSelected();
1142 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1143 ++nTab;
1144 rTabRangeEnd = nTab;
1145 return;
1146 }
1147}
1148
1149static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1150{
1151 if ( pTabMark )
1152 {
1153 // find next range of consecutive selected sheets after rTabRangeEnd
1154 for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
1155 if (pTabMark->GetTableSelect(nTab))
1156 {
1157 rTabRangeStart = nTab;
1158 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1159 ++nTab;
1160 rTabRangeEnd = nTab;
1161 return true;
1162 }
1163 }
1164 return false;
1165}
1166
1167bool ScDocument::CanInsertRow( const ScRange& rRange ) const
1168{
1169 SCCOL nStartCol = rRange.aStart.Col();
1170 SCROW nStartRow = rRange.aStart.Row();
1171 SCTAB nStartTab = rRange.aStart.Tab();
1172 SCCOL nEndCol = rRange.aEnd.Col();
1173 SCROW nEndRow = rRange.aEnd.Row();
1174 SCTAB nEndTab = rRange.aEnd.Tab();
1175 PutInOrder( nStartCol, nEndCol );
1176 PutInOrder( nStartRow, nEndRow );
1177 PutInOrder( nStartTab, nEndTab );
1178 SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
1179
1180 bool bTest = true;
1181 for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
1182 if (maTabs[i])
1183 bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );
1184
1185 return bTest;
1186}
1187
1188namespace {
1189
1190struct SetDirtyIfPostponedHandler
1191{
1192 void operator() (const ScTableUniquePtr & p)
1193 {
1194 if (p)
1195 p->SetDirtyIfPostponed();
1196 }
1197};
1198
1199struct BroadcastRecalcOnRefMoveHandler
1200{
1201 void operator() (const ScTableUniquePtr & p)
1202 {
1203 if (p)
1204 p->BroadcastRecalcOnRefMove();
1205 }
1206};
1207
1208struct BroadcastRecalcOnRefMoveGuard
1209{
1210 explicit BroadcastRecalcOnRefMoveGuard( ScDocument* pDoc ) :
1211 aSwitch( *pDoc, false),
1212 aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
1213 {
1214 }
1215
1216private:
1217 sc::AutoCalcSwitch aSwitch; // first for ctor/dtor order, destroy second
1218 ScBulkBroadcast aBulk; // second for ctor/dtor order, destroy first
1219};
1220
1221}
1222
1223bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
1224 SCCOL nEndCol, SCTAB nEndTab,
1225 SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
1226 const ScMarkData* pTabMark )
1227{
1228 SCTAB i;
1229
1230 PutInOrder( nStartCol, nEndCol );
1231 PutInOrder( nStartTab, nEndTab );
1232 if ( pTabMark )
1233 {
1234 nStartTab = 0;
1235 nEndTab = GetTableCount() - 1;
1236 }
1237
1238 bool bTest = true;
1239 bool bRet = false;
1240 bool bOldAutoCalc = GetAutoCalc();
1241 SetAutoCalc( false ); // avoid multiple calculations
1242 bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
1244 for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
1245 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1246 bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
1247 if (bTest)
1248 {
1249 // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
1250 // aren't shifted that would be rebuild at UpdateReference
1251
1252 // handle chunks of consecutive selected sheets together
1253 SCTAB nTabRangeStart = nStartTab;
1254 SCTAB nTabRangeEnd = nEndTab;
1255 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1256 ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
1257 sc::EndListeningContext aEndListenCxt(*this);
1258
1259 std::vector<ScAddress> aGroupPos;
1260 do
1261 {
1262 aShiftedRange.aStart.SetTab(nTabRangeStart);
1263 aShiftedRange.aEnd.SetTab(nTabRangeEnd);
1264
1265 // Collect all formula groups that will get split by the shifting,
1266 // and end all their listening. Record the position of the top
1267 // cell of the topmost group, and the position of the bottom cell
1268 // of the bottommost group.
1269 EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);
1270
1271 UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
1272 }
1273 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1274
1275 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1276
1277 sc::RefUpdateContext aCxt(*this);
1278 aCxt.meMode = URM_INSDEL;
1279 aCxt.maRange = aShiftedRange;
1280 aCxt.mnRowDelta = nSize;
1281 do
1282 {
1283 aCxt.maRange.aStart.SetTab(nTabRangeStart);
1284 aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
1285 UpdateReference(aCxt, pRefUndoDoc, false); // without drawing objects
1286 }
1287 while (lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1288
1289 // UpdateReference should have set "needs listening" flags to those
1290 // whose references have been modified. We also need to set this flag
1291 // to those that were in the groups that got split by shifting.
1292 SetNeedsListeningGroups(aGroupPos);
1293
1294 for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
1295 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1296 maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
1297
1298 // UpdateRef for drawing layer must be after inserting,
1299 // when the new row heights are known.
1300 for (i=nStartTab; i<=nEndTab && i < GetTableCount(); i++)
1301 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1302 maTabs[i]->UpdateDrawRef( URM_INSDEL,
1303 nStartCol, nStartRow, nStartTab, nEndCol, MaxRow(), nEndTab,
1304 0, static_cast<SCROW>(nSize), 0 );
1305
1306 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1307 { // A new Listening is needed when references to deleted ranges are restored,
1308 // previous Listeners were removed in FormulaCell UpdateReference.
1310 }
1311 else
1312 { // Listeners have been removed in UpdateReference
1314
1315 // At least all cells using range names pointing relative to the
1316 // moved range must be recalculated, and all cells marked postponed
1317 // dirty.
1318 for (const auto& a : maTabs)
1319 {
1320 if (a)
1321 a->SetDirtyIfPostponed();
1322 }
1323
1324 {
1325 BroadcastRecalcOnRefMoveGuard g(this);
1326 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1327 }
1328 }
1329 bRet = true;
1330 }
1331 EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
1332 SetAutoCalc( bOldAutoCalc );
1333 if ( bRet && pChartListenerCollection )
1334 pChartListenerCollection->UpdateDirtyCharts();
1335 return bRet;
1336}
1337
1338bool ScDocument::InsertRow( const ScRange& rRange )
1339{
1340 return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1341 rRange.aEnd.Col(), rRange.aEnd.Tab(),
1342 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1343}
1344
1345void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
1346 SCCOL nEndCol, SCTAB nEndTab,
1347 SCROW nStartRow, SCSIZE nSize,
1348 ScDocument* pRefUndoDoc, bool* pUndoOutline,
1349 const ScMarkData* pTabMark )
1350{
1351 SCTAB i;
1352
1353 PutInOrder( nStartCol, nEndCol );
1354 PutInOrder( nStartTab, nEndTab );
1355 if ( pTabMark )
1356 {
1357 nStartTab = 0;
1358 nEndTab = GetTableCount() - 1;
1359 }
1360
1361 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1362
1363 // handle chunks of consecutive selected sheets together
1364 SCTAB nTabRangeStart = nStartTab;
1365 SCTAB nTabRangeEnd = nEndTab;
1366 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1367 do
1368 {
1369 if ( ValidRow(nStartRow+nSize) )
1370 {
1372 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1373 ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
1375 ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
1376 ScAddress( nEndCol, MaxRow(), nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
1377 }
1378 else
1380 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1381 ScAddress( nEndCol, MaxRow(), nTabRangeEnd ) ) );
1382 }
1383 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1384
1385 sc::RefUpdateContext aCxt(*this);
1386 const bool bLastRowIncluded = (static_cast<SCROW>(nStartRow + nSize) == GetMaxRowCount() && ValidRow(nStartRow));
1387 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1388 {
1389 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1390 aCxt.meMode = URM_INSDEL;
1391 aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
1392 if (bLastRowIncluded)
1393 {
1394 // Last row is included, shift a virtually non-existent row in.
1395 aCxt.maRange = ScRange( nStartCol, GetMaxRowCount(), nTabRangeStart, nEndCol, GetMaxRowCount(), nTabRangeEnd);
1396 }
1397 else
1398 {
1399 aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
1400 }
1401 do
1402 {
1403 UpdateReference(aCxt, pRefUndoDoc, true, false);
1404 }
1405 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1406 }
1407
1408 if (pUndoOutline)
1409 *pUndoOutline = false;
1410
1411 // Keep track of the positions of all formula groups that have been joined
1412 // during row deletion.
1413 std::vector<ScAddress> aGroupPos;
1414
1415 for ( i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
1416 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1417 maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
1418
1419 // Newly joined groups have some of their members still listening. We
1420 // need to make sure none of them are listening.
1421 EndListeningGroups(aGroupPos);
1422
1423 // Mark all joined groups for group listening.
1424 SetNeedsListeningGroups(aGroupPos);
1425
1426 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1427 {
1428 // Listeners have been removed in UpdateReference
1430
1431 // At least all cells using range names pointing relative to the moved
1432 // range must be recalculated, and all cells marked postponed dirty.
1433 for (const auto& a : maTabs)
1434 {
1435 if (a)
1436 a->SetDirtyIfPostponed();
1437 }
1438
1439 {
1440 BroadcastRecalcOnRefMoveGuard g(this);
1441 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1442 }
1443 }
1444
1446 pChartListenerCollection->UpdateDirtyCharts();
1447}
1448
1449void ScDocument::DeleteRow( const ScRange& rRange )
1450{
1451 DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1452 rRange.aEnd.Col(), rRange.aEnd.Tab(),
1453 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1454}
1455
1456bool ScDocument::CanInsertCol( const ScRange& rRange ) const
1457{
1458 SCCOL nStartCol = rRange.aStart.Col();
1459 SCROW nStartRow = rRange.aStart.Row();
1460 SCTAB nStartTab = rRange.aStart.Tab();
1461 SCCOL nEndCol = rRange.aEnd.Col();
1462 SCROW nEndRow = rRange.aEnd.Row();
1463 SCTAB nEndTab = rRange.aEnd.Tab();
1464 PutInOrder( nStartCol, nEndCol );
1465 PutInOrder( nStartRow, nEndRow );
1466 PutInOrder( nStartTab, nEndTab );
1467 SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);
1468
1469 bool bTest = true;
1470 for (SCTAB i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
1471 if (maTabs[i])
1472 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1473
1474 return bTest;
1475}
1476
1477bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
1478 SCROW nEndRow, SCTAB nEndTab,
1479 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1480 const ScMarkData* pTabMark )
1481{
1482 SCTAB i;
1483
1484 PutInOrder( nStartRow, nEndRow );
1485 PutInOrder( nStartTab, nEndTab );
1486 if ( pTabMark )
1487 {
1488 nStartTab = 0;
1489 nEndTab = GetTableCount() - 1;
1490 }
1491
1492 bool bTest = true;
1493 bool bRet = false;
1494 bool bOldAutoCalc = GetAutoCalc();
1495 SetAutoCalc( false ); // avoid multiple calculations
1496 bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
1498 for ( i = nStartTab; i <= nEndTab && bTest && i < GetTableCount(); i++)
1499 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1500 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1501 if (bTest)
1502 {
1503 // handle chunks of consecutive selected sheets together
1504 SCTAB nTabRangeStart = nStartTab;
1505 SCTAB nTabRangeEnd = nEndTab;
1506 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1507 do
1508 {
1510 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1511 ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
1512 }
1513 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1514
1515 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1516
1517 sc::RefUpdateContext aCxt(*this);
1518 aCxt.meMode = URM_INSDEL;
1519 aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MaxCol(), nEndRow, nTabRangeEnd);
1520 aCxt.mnColDelta = nSize;
1521 do
1522 {
1523 UpdateReference(aCxt, pRefUndoDoc, true, false);
1524 }
1525 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1526
1527 for (i = nStartTab; i <= nEndTab && i < GetTableCount(); i++)
1528 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1529 maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);
1530
1531 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1532 { // A new Listening is needed when references to deleted ranges are restored,
1533 // previous Listeners were removed in FormulaCell UpdateReference.
1535 }
1536 else
1537 {
1538 // Listeners have been removed in UpdateReference
1540 // At least all cells using range names pointing relative to the
1541 // moved range must be recalculated, and all cells marked postponed
1542 // dirty.
1543 {
1544 ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
1545 std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
1546 }
1547 // Cells containing functions such as CELL, COLUMN or ROW may have
1548 // changed their values on relocation. Broadcast them.
1549 {
1550 BroadcastRecalcOnRefMoveGuard g(this);
1551 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1552 }
1553 }
1554 bRet = true;
1555 }
1556 EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
1557 SetAutoCalc( bOldAutoCalc );
1558 if ( bRet && pChartListenerCollection )
1559 pChartListenerCollection->UpdateDirtyCharts();
1560 return bRet;
1561}
1562
1563bool ScDocument::InsertCol( const ScRange& rRange )
1564{
1565 return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
1566 rRange.aEnd.Row(), rRange.aEnd.Tab(),
1567 rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
1568}
1569
1570void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
1571 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1572 bool* pUndoOutline, const ScMarkData* pTabMark )
1573{
1574 SCTAB i;
1575
1576 PutInOrder( nStartRow, nEndRow );
1577 PutInOrder( nStartTab, nEndTab );
1578 if ( pTabMark )
1579 {
1580 nStartTab = 0;
1581 nEndTab = GetTableCount() - 1;
1582 }
1583
1584 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1585 ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
1586
1587 // handle chunks of consecutive selected sheets together
1588 SCTAB nTabRangeStart = nStartTab;
1589 SCTAB nTabRangeEnd = nEndTab;
1590 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1591 do
1592 {
1593 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
1594 {
1596 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1597 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
1599 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
1600 ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
1601 }
1602 else
1604 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1605 ScAddress( MaxCol(), nEndRow, nTabRangeEnd ) ) );
1606 }
1607 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1608
1609 sc::RefUpdateContext aCxt(*this);
1610 const bool bLastColIncluded = (static_cast<SCCOL>(nStartCol + nSize) == GetMaxColCount() && ValidCol(nStartCol));
1611 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1612 {
1613 lcl_GetFirstTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount());
1614 aCxt.meMode = URM_INSDEL;
1615 aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
1616 if (bLastColIncluded)
1617 {
1618 // Last column is included, shift a virtually non-existent column in.
1619 aCxt.maRange = ScRange( GetMaxColCount(), nStartRow, nTabRangeStart, GetMaxColCount(), nEndRow, nTabRangeEnd);
1620 }
1621 else
1622 {
1623 aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
1624 MaxCol(), nEndRow, nTabRangeEnd);
1625 }
1626 do
1627 {
1628 UpdateReference(aCxt, pRefUndoDoc, true, false);
1629 }
1630 while (lcl_GetNextTabRange(nTabRangeStart, nTabRangeEnd, pTabMark, GetTableCount()));
1631 }
1632
1633 if (pUndoOutline)
1634 *pUndoOutline = false;
1635
1636 for (i = nStartTab; i <= nEndTab && i < GetTableCount(); ++i)
1637 {
1638 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1639 maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
1640 }
1641
1642 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1643 {
1644 // Listeners have been removed in UpdateReference
1646
1647 // At least all cells using range names pointing relative to the moved
1648 // range must be recalculated, and all cells marked postponed dirty.
1649 for (const auto& a : maTabs)
1650 {
1651 if (a)
1652 a->SetDirtyIfPostponed();
1653 }
1654
1655 {
1656 BroadcastRecalcOnRefMoveGuard g(this);
1657 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1658 }
1659 }
1660
1662 pChartListenerCollection->UpdateDirtyCharts();
1663}
1664
1665void 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
1675static 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
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,
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
1746{
1747 SCTAB nTab = rPos.Tab();
1748 if (ScTable* pTable = FetchTable(nTab))
1749 return pTable->ResolveStaticReference(rPos.Col(), rPos.Row());
1750 return formula::FormulaTokenRef();
1751}
1752
1754{
1755 SCTAB nTab = rRange.aStart.Tab();
1756 if (nTab != rRange.aEnd.Tab() || !HasTable(nTab))
1757 return formula::FormulaTokenRef();
1758
1759 return maTabs[nTab]->ResolveStaticReference(
1760 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
1761}
1762
1764{
1765 SCTAB nTab = rPos.Tab();
1766 if (ScTable* pTable = FetchTable(nTab))
1767 return pTable->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1768 return formula::VectorRefArray();
1769}
1770
1771#ifdef DBG_UTIL
1773{
1774 SCTAB nTab = rPos.Tab();
1775 assert(HasTable(nTab));
1776 if (ScTable* pTable = FetchTable(nTab))
1777 return pTable->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1778}
1779#endif
1780
1782{
1783 assert(nAdjustHeightLock > 0);
1784 if(nAdjustHeightLock > 0)
1786}
1787
1789{
1790 SCTAB nTab = rPos.Tab();
1791 if (ScTable* pTable = FetchTable(nTab))
1792 return pTable->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup);
1793 return false;
1794}
1795
1796bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
1797{
1798 if ( rOld == rNew )
1799 return true;
1800
1801 bool bOk = true;
1802 bool bInsCol,bDelCol,bInsRow,bDelRow;
1803 ScRange aColRange,aRowRange;
1804 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1805
1806 if ( bInsCol && !CanInsertCol( aColRange ) ) // Cells at the edge ?
1807 bOk = false;
1808 if ( bInsRow && !CanInsertRow( aRowRange ) ) // Cells at the edge ?
1809 bOk = false;
1810
1811 if ( bInsCol || bDelCol )
1812 {
1813 aColRange.aEnd.SetCol(MaxCol());
1814 if ( HasPartOfMerged(aColRange) )
1815 bOk = false;
1816 }
1817 if ( bInsRow || bDelRow )
1818 {
1819 aRowRange.aEnd.SetRow(MaxRow());
1820 if ( HasPartOfMerged(aRowRange) )
1821 bOk = false;
1822 }
1823
1824 return bOk;
1825}
1826
1827void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
1828{
1829 if (bClear)
1831
1832 bool bInsCol,bDelCol,bInsRow,bDelRow;
1833 ScRange aColRange,aRowRange;
1834 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1835
1836 if ( bInsCol )
1837 InsertCol( aColRange ); // First insert columns
1838 if ( bInsRow )
1839 InsertRow( aRowRange );
1840
1841 if ( bDelRow )
1842 DeleteRow( aRowRange ); // First delete rows
1843 if ( bDelCol )
1844 DeleteCol( aColRange );
1845
1846 // Expand references to inserted rows
1847
1848 if ( bInsCol || bInsRow )
1849 {
1850 ScRange aGrowSource = rOld;
1851 aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
1852 aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
1853 SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
1854 SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
1855 UpdateGrow( aGrowSource, nGrowX, nGrowY );
1856 }
1857}
1858
1860 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
1861 InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
1862{
1863 sc::AutoCalcSwitch aACSwitch(*this, false);
1864
1865 PutInOrder( nCol1, nCol2 );
1866 PutInOrder( nRow1, nRow2 );
1867
1868 std::vector<ScAddress> aGroupPos;
1869 // Destroy and reconstruct listeners only if content is affected.
1870 bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
1871 if (bDelContent)
1872 {
1873 // Record the positions of top and/or bottom formula groups that intersect
1874 // the area borders.
1875 sc::EndListeningContext aCxt(*this);
1876 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1877 for (SCTAB i = 0; i < GetTableCount(); i++)
1878 {
1879 if (rMark.GetTableSelect(i))
1880 {
1881 aRange.aStart.SetTab(i);
1882 aRange.aEnd.SetTab(i);
1883
1884 EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
1885 }
1886 }
1888 }
1889
1890 for (SCTAB i = 0; i < GetTableCount(); i++)
1891 if (maTabs[i])
1892 if ( rMark.GetTableSelect(i) || bIsUndo )
1893 maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
1894
1895 if (!bDelContent)
1896 return;
1897
1898 // Re-start listeners on those top bottom groups that have been split.
1899 SetNeedsListeningGroups(aGroupPos);
1901
1902 // If formula groups were split their listeners were destroyed and may
1903 // need to be notified now that they're restored, ScTable::DeleteArea()
1904 // couldn't do that.
1905 if (aGroupPos.empty())
1906 return;
1907
1908 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1909 for (SCTAB i = 0; i < GetTableCount(); i++)
1910 {
1911 if (rMark.GetTableSelect(i))
1912 {
1913 aRange.aStart.SetTab(i);
1914 aRange.aEnd.SetTab(i);
1915 SetDirty( aRange, true);
1916 }
1917 }
1918}
1919
1921 SCCOL nCol2, SCROW nRow2,
1922 SCTAB nTab, InsertDeleteFlags nDelFlag)
1923{
1924 PutInOrder( nCol1, nCol2 );
1925 PutInOrder( nRow1, nRow2 );
1926 if (ScTable* pTable = FetchTable(nTab))
1927 {
1928 bool bOldAutoCalc = GetAutoCalc();
1929 SetAutoCalc( false ); // avoid multiple calculations
1930 pTable->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag);
1931 SetAutoCalc( bOldAutoCalc );
1932 }
1933}
1934
1936{
1937 for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ )
1938 DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(),
1939 rRange.aEnd.Col(), rRange.aEnd.Row(),
1940 nTab, nDelFlag );
1941}
1942
1943void ScDocument::InitUndoSelected(const ScDocument& rSrcDoc, const ScMarkData& rTabSelection,
1944 bool bColInfo, bool bRowInfo )
1945{
1946 if (bIsUndo)
1947 {
1948 Clear();
1949
1950 SharePooledResources(&rSrcDoc);
1951
1952 for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++)
1953 if ( rTabSelection.GetTableSelect( nTab ) )
1954 {
1955 ScTableUniquePtr pTable(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
1956 if (nTab < GetTableCount())
1957 maTabs[nTab] = std::move(pTable);
1958 else
1959 maTabs.push_back(std::move(pTable));
1960 }
1961 else
1962 {
1963 if (nTab < GetTableCount())
1964 maTabs[nTab]=nullptr;
1965 else
1966 maTabs.push_back(nullptr);
1967 }
1968 }
1969 else
1970 {
1971 OSL_FAIL("InitUndo");
1972 }
1973}
1974
1975void ScDocument::InitUndo( const ScDocument& rSrcDoc, SCTAB nTab1, SCTAB nTab2,
1976 bool bColInfo, bool bRowInfo )
1977{
1978 if (!bIsUndo)
1979 {
1980 OSL_FAIL("InitUndo");
1981 return;
1982 }
1983
1984 Clear();
1985
1986 // Undo document shares its pooled resources with the source document.
1987 SharePooledResources(&rSrcDoc);
1988
1989 if (rSrcDoc.mpShell->GetMedium())
1991
1992 if (nTab2 >= GetTableCount())
1993 maTabs.resize(nTab2 + 1);
1994 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
1995 {
1996 maTabs[nTab].reset(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
1997 }
1998}
1999
2000void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo )
2001{
2002 if (!bIsUndo)
2003 {
2004 OSL_FAIL("AddUndoTab");
2005 return;
2006 }
2007
2008 if (nTab2 >= GetTableCount())
2009 {
2010 maTabs.resize(nTab2+1);
2011 }
2012
2013 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
2014 if (!maTabs[nTab])
2015 {
2016 maTabs[nTab].reset( new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo) );
2017 }
2018}
2019
2020void ScDocument::SetCutMode( bool bVal )
2021{
2022 if (bIsClip)
2023 GetClipParam().mbCutMode = bVal;
2024 else
2025 {
2026 OSL_FAIL("SetCutMode without bIsClip");
2027 }
2028}
2029
2031{
2032 if (bIsClip)
2033 return GetClipParam().mbCutMode;
2034 else
2035 {
2036 OSL_FAIL("IsCutMode without bIsClip");
2037 return false;
2038 }
2039}
2040
2042 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2043 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2044 const ScMarkData* pMarks, bool bColRowFlags )
2045{
2046 if (ValidTab(nTab1) && ValidTab(nTab2))
2047 {
2048 ScRange aThisRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2049 CopyToDocument(aThisRange, nFlags, bOnlyMarked, rDestDoc, pMarks, bColRowFlags);
2050 }
2051}
2052
2054 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2055 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2056{
2057 PutInOrder( nCol1, nCol2 );
2058 PutInOrder( nRow1, nRow2 );
2059 PutInOrder( nTab1, nTab2 );
2060 if (!(ValidTab(nTab1) && ValidTab(nTab2)))
2061 return;
2062
2063 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2064
2065 if (nTab1 > 0)
2066 CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2067
2068 sc::CopyToDocContext aCxt(rDestDoc);
2069 assert(nTab2 < GetTableCount() && nTab2 < rDestDoc.GetTableCount());
2070 for (SCTAB i = nTab1; i <= nTab2; i++)
2071 {
2072 if (maTabs[i] && rDestDoc.maTabs[i])
2073 maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
2074 bOnlyMarked, rDestDoc.maTabs[i].get());
2075 }
2076
2077 if (nTab2 < MAXTAB)
2078 CopyToDocument(0, 0, nTab2+1, MaxCol(), MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc);
2079}
2080
2082 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2083 const ScMarkData* pMarks, bool bColRowFlags)
2084{
2085 ScRange aNewRange = rRange;
2086 aNewRange.PutInOrder();
2087
2088 if (rDestDoc.aDocName.isEmpty())
2089 rDestDoc.aDocName = aDocName;
2090
2091 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2092 ScBulkBroadcast aBulkBroadcast(rDestDoc.GetBASM(), SfxHintId::ScDataChanged);
2093 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
2094
2095 sc::CopyToDocContext aCxt(rDestDoc);
2096 aCxt.setStartListening(false);
2097
2098 SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rDestDoc.GetTableCount());
2099 for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++)
2100 {
2101 ScTable* pTab = FetchTable(i);
2102 ScTable* pDestTab = rDestDoc.FetchTable(i);
2103 if (!pTab || !pDestTab)
2104 continue;
2105
2106 pTab->CopyToTable(
2107 aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2108 nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags,
2109 /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true);
2110 }
2111
2112 rDestDoc.StartAllListeners(aNewRange);
2113}
2114
2116 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2117{
2118 sc::AutoCalcSwitch aAutoCalcSwitch(*this, false);
2119
2120 ScRange aNewRange = rRange;
2121 aNewRange.PutInOrder();
2122 SCTAB nTab1 = aNewRange.aStart.Tab();
2123 SCTAB nTab2 = aNewRange.aEnd.Tab();
2124
2125 sc::CopyToDocContext aCxt(rDestDoc);
2126 if (nTab1 > 0)
2127 CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2128
2129 SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rDestDoc.GetTableCount());
2130 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
2131 {
2132 if (maTabs[i] && rDestDoc.maTabs[i])
2133 maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(),
2134 aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2135 nFlags, bOnlyMarked, rDestDoc.maTabs[i].get());
2136 }
2137
2138 if (nTab2 < GetTableCount())
2139 CopyToDocument(0, 0 , nTab2+1, MaxCol(), MaxRow(), GetTableCount(), InsertDeleteFlags::FORMULA, false, rDestDoc);
2140}
2141
2143 ScDocument* pClipDoc, const ScMarkData* pMarks,
2144 bool bKeepScenarioFlags, bool bIncludeObjects )
2145{
2146 OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" );
2147
2148 if (bIsClip)
2149 return;
2150
2151 if (!pClipDoc)
2152 {
2153 SAL_WARN("sc", "CopyToClip: no ClipDoc");
2154 pClipDoc = ScModule::GetClipDoc();
2155 }
2156
2157 if (mpShell->GetMedium())
2158 {
2160 // for unsaved files use the title name and adjust during save of file
2161 if (pClipDoc->maFileURL.isEmpty())
2162 pClipDoc->maFileURL = mpShell->GetName();
2163 }
2164 else
2165 {
2166 pClipDoc->maFileURL = mpShell->GetName();
2167 }
2168
2169 //init maTabNames
2170 for (const auto& rxTab : maTabs)
2171 {
2172 if( rxTab )
2173 {
2174 OUString aTabName = rxTab->GetName();
2175 pClipDoc->maTabNames.push_back(aTabName);
2176 }
2177 else
2178 pClipDoc->maTabNames.emplace_back();
2179 }
2180
2181 pClipDoc->aDocName = aDocName;
2182 pClipDoc->SetClipParam(rClipParam);
2183 ScRange aClipRange = rClipParam.getWholeRange();
2184 SCTAB nEndTab = GetTableCount();
2185
2186 pClipDoc->ResetClip(this, pMarks);
2187
2188 sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
2189 CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);
2190
2191 for (SCTAB i = 0; i < nEndTab; ++i)
2192 {
2193 if (!maTabs[i] || i >= pClipDoc->GetTableCount() || !pClipDoc->maTabs[i])
2194 continue;
2195
2196 if ( pMarks && !pMarks->GetTableSelect(i) )
2197 continue;
2198
2199 maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
2200
2201 if (mpDrawLayer && bIncludeObjects)
2202 {
2203 // also copy drawing objects
2204 tools::Rectangle aObjRect = GetMMRect(
2205 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
2206 mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
2207 }
2208 }
2209
2210 // Make sure to mark overlapped cells.
2211 pClipDoc->ExtendMerge(aClipRange, true);
2212}
2213
2214void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument& rDestDoc)
2215{
2216 ScTable* pSrcTab = rSrcRange.aStart.Tab() < GetTableCount() ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr;
2217 ScTable* pDestTab = nDestTab < rDestDoc.GetTableCount() ? rDestDoc.maTabs[nDestTab].get() : nullptr;
2218
2219 if (!pSrcTab || !pDestTab)
2220 return;
2221
2224
2225 pSrcTab->CopyStaticToDocument(
2226 rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(),
2227 aMap, pDestTab);
2228}
2229
2230void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc )
2231{
2232 if (!HasTable(rSrcPos.Tab()) || !rDestDoc.HasTable(rDestPos.Tab()))
2233 return;
2234
2235 ScTable& rSrcTab = *maTabs[rSrcPos.Tab()];
2236 ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()];
2237
2238 rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab);
2239}
2240
2242 SCCOL nCol2, SCROW nRow2,
2243 SCTAB nTab, ScDocument* pClipDoc)
2244{
2245 if (bIsClip)
2246 return;
2247
2248 if (!pClipDoc)
2249 {
2250 SAL_WARN("sc", "CopyTabToClip: no ClipDoc");
2251 pClipDoc = ScModule::GetClipDoc();
2252 }
2253
2254 if (mpShell->GetMedium())
2255 {
2257 // for unsaved files use the title name and adjust during save of file
2258 if (pClipDoc->maFileURL.isEmpty())
2259 pClipDoc->maFileURL = mpShell->GetName();
2260 }
2261 else
2262 {
2263 pClipDoc->maFileURL = mpShell->GetName();
2264 }
2265
2266 //init maTabNames
2267 for (const auto& rxTab : maTabs)
2268 {
2269 if( rxTab )
2270 {
2271 OUString aTabName = rxTab->GetName();
2272 pClipDoc->maTabNames.push_back(aTabName);
2273 }
2274 else
2275 pClipDoc->maTabNames.emplace_back();
2276 }
2277
2278 PutInOrder( nCol1, nCol2 );
2279 PutInOrder( nRow1, nRow2 );
2280
2281 ScClipParam& rClipParam = pClipDoc->GetClipParam();
2282 pClipDoc->aDocName = aDocName;
2283 rClipParam.maRanges.RemoveAll();
2284 rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
2285 pClipDoc->ResetClip( this, nTab );
2286
2287 sc::CopyToClipContext aCxt(*pClipDoc, false);
2288 if (nTab < GetTableCount() && nTab < pClipDoc->GetTableCount())
2289 if (maTabs[nTab] && pClipDoc->maTabs[nTab])
2290 maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get());
2291
2292 pClipDoc->GetClipParam().mbCutMode = false;
2293}
2294
2295void ScDocument::TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
2296 bool bIncludeFiltered)
2297{
2298 OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip,
2299 "TransposeClip with wrong Document" );
2300
2301 // initialize
2302 // -> pTransClip has to be deleted before the original document!
2303
2304 pTransClip->ResetClip(this, nullptr); // all
2305
2306 // Take over range
2307
2308 if (pRangeName)
2309 {
2310 pTransClip->GetRangeName()->clear();
2311 for (const auto& rEntry : *pRangeName)
2312 {
2313 sal_uInt16 nIndex = rEntry.second->GetIndex();
2314 ScRangeData* pData = new ScRangeData(*rEntry.second);
2315 if (pTransClip->pRangeName->insert(pData))
2316 pData->SetIndex(nIndex);
2317 }
2318 }
2319
2320 ScRange aCombinedClipRange = GetClipParam().getWholeRange();
2321
2322 if (!ValidRow(aCombinedClipRange.aEnd.Row() - aCombinedClipRange.aStart.Row()))
2323 {
2324 SAL_WARN("sc", "TransposeClip: Too big");
2325 return;
2326 }
2327
2328 // Transpose of filtered multi range row selection is a special case since filtering
2329 // and selection are in the same dimension (i.e. row).
2330 // The filtered row status and the selection ranges are not available at the same time,
2331 // handle this case specially, do not use GetClipParam().getWholeRange(),
2332 // instead loop through the ranges, calculate the row offset and handle filtered rows and
2333 // create in ScClipParam::transpose() a unified range.
2334 const bool bIsMultiRangeRowFilteredTranspose
2335 = !bIncludeFiltered && GetClipParam().isMultiRange()
2336 && HasFilteredRows(aCombinedClipRange.aStart.Row(), aCombinedClipRange.aEnd.Row(),
2337 aCombinedClipRange.aStart.Tab())
2339
2340 ScRangeList aClipRanges;
2341 if (bIsMultiRangeRowFilteredTranspose)
2342 aClipRanges = GetClipParam().maRanges;
2343 else
2344 aClipRanges = ScRangeList(aCombinedClipRange);
2345
2346 // The data
2347 ScRange aClipRange;
2348 SCROW nRowCount = 0; // next consecutive row
2349 for (size_t j = 0, n = aClipRanges.size(); j < n; ++j)
2350 {
2351 aClipRange = aClipRanges[j];
2352
2353 SCROW nRowOffset = 0;
2354 if (bIsMultiRangeRowFilteredTranspose)
2355 {
2356 // adjust for the rows that are filtered
2357 nRowOffset = nRowCount;
2358
2359 // calculate filtered rows of current clip range
2360 SCROW nRowCountNonFiltered = CountNonFilteredRows(
2361 aClipRange.aStart.Row(), aClipRange.aEnd.Row(), aClipRange.aStart.Tab());
2362 assert(!bIncludeFiltered && "bIsMultiRangeRowFilteredTranspose can only be true if bIncludeFiltered is false");
2363 nRowCount += nRowCountNonFiltered; // for next iteration
2364 }
2365
2366 for (SCTAB i = 0; i < GetTableCount(); i++)
2367 {
2368 if (maTabs[i])
2369 {
2370 OSL_ENSURE(pTransClip->maTabs[i], "TransposeClip: Table not there");
2371 maTabs[i]->TransposeClip(
2372 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(),
2373 aClipRange.aEnd.Row(), aCombinedClipRange.aStart.Row(), nRowOffset,
2374 pTransClip->maTabs[i].get(), nFlags, bAsLink, bIncludeFiltered);
2375
2376 if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) )
2377 {
2378 // Drawing objects are copied to the new area without transposing.
2379 // CopyFromClip is used to adjust the objects to the transposed block's
2380 // cell range area.
2381 // (mpDrawLayer in the original clipboard document is set only if there
2382 // are drawing objects to copy)
2383
2384 // ToDo: Loop over blocks of non-filtered rows in case of filtered rows exist.
2385 pTransClip->InitDrawLayer();
2386 ScAddress aTransposedEnd(
2387 static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row() + aClipRange.aStart.Col()),
2388 static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col() + aClipRange.aStart.Row()), i);
2389 ScRange aDestRange(aClipRange.aStart, aTransposedEnd);
2390 ScAddress aDestStart = aClipRange.aStart;
2391 pTransClip->mpDrawLayer->CopyFromClip(mpDrawLayer.get(), i, aClipRange, aDestStart, aDestRange, true);
2392 }
2393 }
2394 }
2395 }
2396
2397 pTransClip->SetClipParam(GetClipParam());
2398 pTransClip->GetClipParam().transpose(*this, bIncludeFiltered,
2399 bIsMultiRangeRowFilteredTranspose);
2400
2401 // This happens only when inserting...
2402
2403 GetClipParam().mbCutMode = false;
2404}
2405
2406namespace {
2407
2408void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName,
2410{
2411 pClipRangeName->clear();
2412 for (const auto& rEntry : *pRangeName) //TODO: also DB and Pivot regions!!!
2413 {
2414 sal_uInt16 nIndex = rEntry.second->GetIndex();
2415 bool bInUse = (rUsedNames.count(nIndex) > 0);
2416 if (!bInUse)
2417 continue;
2418
2419 ScRangeData* pData = new ScRangeData(*rEntry.second);
2420 if (pClipRangeName->insert(pData))
2421 pData->SetIndex(nIndex);
2422 }
2423}
2424
2425}
2426
2427void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks)
2428{
2429 if (!pRangeName || pRangeName->empty())
2430 return;
2431
2432 sc::UpdatedRangeNames aUsedNames; // indexes of named ranges that are used in the copied cells
2433 SCTAB nMinSizeBothTabs = std::min(GetTableCount(), pClipDoc->GetTableCount());
2434 for (SCTAB i = 0; i < nMinSizeBothTabs; ++i)
2435 if (maTabs[i] && pClipDoc->maTabs[i])
2436 if ( !pMarks || pMarks->GetTableSelect(i) )
2437 maTabs[i]->FindRangeNamesInUse(
2438 rClipRange.aStart.Col(), rClipRange.aStart.Row(),
2439 rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames);
2440
2441 /* TODO: handle also sheet-local names */
2442 sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1));
2443 copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames);
2444}
2445
2447 : mrDoc(rDoc)
2448{
2449 mrDoc.MergeNumberFormatter(rSrcDoc);
2450}
2451
2453{
2455 mrDoc.pFormatExchangeList = nullptr;
2456}
2457
2459{
2461 mpFormulaGroupCxt.reset();
2462}
2463
2465{
2466 ScTable* pTab = FetchTable(rPos.Tab());
2467 if (!pTab)
2468 return nullptr;
2469
2470 return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2471}
2472
2474{
2475 const ScTable* pTab = FetchTable(rPos.Tab());
2476 if (!pTab)
2477 return nullptr;
2478
2479 return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2480}
2481
2483{
2484 ScTable* pTab = FetchTable(rTopPos.Tab());
2485 if (!pTab || nLength <= 0)
2486 return;
2487
2488 pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
2489}
2490
2491#if DUMP_COLUMN_STORAGE
2492void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
2493{
2494 const ScTable* pTab = FetchTable(nTab);
2495 if (!pTab)
2496 return;
2497
2498 pTab->DumpColumnStorage(nCol);
2499}
2500#endif
2501
2503{
2504 return ValidTab(nTab)
2505 && nTab < GetTableCount()
2506 && maTabs[nTab];
2507}
2508
2510{
2511 if (!HasTable(nTab))
2512 return nullptr;
2513
2514 return maTabs[nTab].get();
2515}
2516
2518{
2519 if (!HasTable(nTab))
2520 return nullptr;
2521
2522 return maTabs[nTab].get();
2523}
2524
2526{
2527 if (ScTable* pTable = FetchTable(nTab))
2528 return pTable->GetWritableColumnsRange(nColBegin, nColEnd);
2529
2530 SAL_WARN("sc", "GetWritableColumnsRange() called for non-existent table");
2531 return ScColumnsRange(-1, -1);
2532}
2533
2535{
2536 if (const ScTable* pTable = FetchTable(nTab))
2537 return pTable->GetAllocatedColumnsRange(nColBegin, nColEnd);
2538 return ScColumnsRange(-1, -1);
2539}
2540
2542{
2543 if (const ScTable* pTable = FetchTable(nTab))
2544 return pTable->GetColumnsRange(nColBegin, nColEnd);
2545 return ScColumnsRange(-1, -1);
2546}
2547
2549{
2550 SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable();
2551 SvNumberFormatter* pOtherFormatter = rSrcDoc.mxPoolHelper->GetFormTable();
2552 if (pOtherFormatter && pOtherFormatter != pThisFormatter)
2553 {
2554 SvNumberFormatterIndexTable* pExchangeList =
2555 pThisFormatter->MergeFormatter(*pOtherFormatter);
2556 if (!pExchangeList->empty())
2557 {
2559 pFormatExchangeList = pExchangeList;
2560 }
2561 }
2562}
2563
2565{
2566 if (!mpClipParam)
2567 mpClipParam.reset(new ScClipParam);
2568
2569 return *mpClipParam;
2570}
2571
2573{
2574 mpClipParam.reset(new ScClipParam(rParam));
2575}
2576
2578{
2579 if (bIsClip || mpShell == nullptr || mpShell->IsLoading())
2580 return false;
2581
2582 ScDocument* pClipDoc = ScModule::GetClipDoc();
2583 return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() &&
2584 mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
2585}
2586
2589 SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
2590{
2591 if (ScTable* pTable = FetchTable(nTab))
2592 pTable->StartListeningFormulaCells(rStartCxt, rEndCxt, nCol1, nRow1, nCol2, nRow2);
2593}
2594
2596 SCCOL nCol2, SCROW nRow2,
2597 const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
2598{
2599 if (!(nInsFlag & InsertDeleteFlags::CONTENTS))
2600 return;
2601
2602 auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
2603
2604 sc::StartListeningContext aStartCxt(*this, pSet);
2605 sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
2606
2607 for (SCTAB nTab : rMark)
2608 StartListeningFromClip(aStartCxt, aEndCxt, nTab, nCol1, nRow1, nCol2, nRow2);
2609}
2610
2612 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
2613 InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
2614{
2615 if (nInsFlag & InsertDeleteFlags::CONTENTS)
2616 {
2617 SCTAB nMax = GetTableCount();
2618 for (const auto& rTab : rMark)
2619 {
2620 if (rTab >= nMax)
2621 break;
2622 if (maTabs[rTab])
2623 maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
2624 }
2625 }
2626}
2627
2629{
2630 if (ScTable* pTable = FetchTable(nTab))
2631 return pTable->InitColumnBlockPosition(rBlockPos, nCol);
2632 return false;
2633}
2634
2636 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
2637 const ScMarkData& rMark, SCCOL nDx, SCROW nDy )
2638{
2639 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2640 SCTAB nTabEnd = rCxt.getTabEnd();
2641 SCTAB nClipTab = 0;
2642 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); i++)
2643 {
2644 if (maTabs[i] && rMark.GetTableSelect(i) )
2645 {
2646 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2647
2648 maTabs[i]->CopyFromClip(
2649 rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get());
2650
2652 {
2653 // also copy drawing objects
2654
2655 // drawing layer must be created before calling CopyFromClip
2656 // (ScDocShell::MakeDrawLayer also does InitItems etc.)
2657 OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" );
2658 if ( mpDrawLayer )
2659 {
2660 // For GetMMRect, the row heights in the target document must already be valid
2661 // (copied in an extra step before pasting, or updated after pasting cells, but
2662 // before pasting objects).
2663 ScRange aSourceRange(nCol1 - nDx, nRow1 - nDy, nClipTab, nCol2 - nDx, nRow2 - nDy, nClipTab);
2664 ScRange aDestRange(nCol1, nRow1, i, nCol2, nRow2, i);
2665 mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRange,
2666 ScAddress( nCol1, nRow1, i ), aDestRange);
2667 }
2668 }
2669
2670 nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2671 }
2672 }
2674 return;
2675
2676 nClipTab = 0;
2677 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); i++)
2678 {
2679 if (maTabs[i] && rMark.GetTableSelect(i) )
2680 {
2681 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2682 SCTAB nDz = i - nClipTab;
2683
2684 // ranges of consecutive selected tables (in clipboard and dest. doc)
2685 // must be handled in one UpdateReference call
2686 SCTAB nFollow = 0;
2687 while ( i + nFollow < nTabEnd
2688 && rMark.GetTableSelect( i + nFollow + 1 )
2689 && nClipTab + nFollow < MAXTAB
2690 && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
2691 ++nFollow;
2692
2693 sc::RefUpdateContext aRefCxt(*this, rCxt.getClipDoc());
2694 aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
2695 aRefCxt.mnColDelta = nDx;
2696 aRefCxt.mnRowDelta = nDy;
2697 aRefCxt.mnTabDelta = nDz;
2698 aRefCxt.setBlockPositionReference(rCxt.getBlockPositionSet()); // share mdds position caching
2699 if (rCxt.getClipDoc()->GetClipParam().mbCutMode)
2700 {
2701 // Update references only if cut originates from the same
2702 // document we are pasting into.
2703 if (rCxt.getClipDoc()->GetPool() == GetPool())
2704 {
2705 bool bOldInserting = IsInsertingFromOtherDoc();
2707 aRefCxt.meMode = URM_MOVE;
2708 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2709
2710 // For URM_MOVE group listeners may have been removed,
2711 // re-establish them.
2712 if (!aRefCxt.maRegroupCols.empty())
2713 {
2714 /* TODO: holding the ColumnSet in a shared_ptr at
2715 * RefUpdateContext would eliminate the need of
2716 * copying it here. */
2717 auto pColSet = std::make_shared<sc::ColumnSet>( aRefCxt.maRegroupCols);
2718 StartNeededListeners( pColSet);
2719 }
2720
2721 SetInsertingFromOtherDoc( bOldInserting);
2722 }
2723 }
2724 else
2725 {
2726 aRefCxt.meMode = URM_COPY;
2727 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2728 }
2729
2730 nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size());
2731 i = sal::static_int_cast<SCTAB>( i + nFollow );
2732 }
2733 }
2734}
2735
2737 SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
2738 SCCOL nDx, SCROW& rClipStartRow, SCROW nClipEndRow)
2739{
2740 // call CopyBlockFromClip for ranges of consecutive non-filtered rows
2741 // nCol1/nRow1 etc. is in target doc
2742
2743 // filtered state is taken from first used table in clipboard (as in GetClipArea)
2744 SCTAB nFlagTab = 0;
2745 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2746 while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] )
2747 ++nFlagTab;
2748
2749 SCROW nSourceRow = rClipStartRow;
2750 SCROW nSourceEnd = nClipEndRow;
2751 SCROW nDestRow = nRow1;
2752 SCROW nFilteredRows = 0;
2753
2754 while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 )
2755 {
2756 // skip filtered rows
2757 SCROW nSourceRowOriginal = nSourceRow;
2758 nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab);
2759 nFilteredRows += nSourceRow - nSourceRowOriginal;
2760
2761 if ( nSourceRow <= nSourceEnd )
2762 {
2763 // look for more non-filtered rows following
2764 SCROW nLastRow = nSourceRow;
2765 (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow);
2766 SCROW nFollow = nLastRow - nSourceRow;
2767
2768 if (nFollow > nSourceEnd - nSourceRow)
2769 nFollow = nSourceEnd - nSourceRow;
2770 if (nFollow > nRow2 - nDestRow)
2771 nFollow = nRow2 - nDestRow;
2772
2773 SCROW nNewDy = nDestRow - nSourceRow;
2775 rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy);
2776
2777 nSourceRow += nFollow + 1;
2778 nDestRow += nFollow + 1;
2779 }
2780 }
2781 rClipStartRow = nSourceRow;
2782 return nFilteredRows;
2783}
2784
2785namespace {
2786
2787class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
2788{
2789 ScDocument& mrDoc;
2790 ScColumn* mpCol;
2791
2792public:
2793 explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {}
2794
2795 virtual void startColumn( ScColumn* pCol ) override
2796 {
2797 mpCol = pCol;
2798 }
2799
2800 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2801 {
2802 if (!bVal)
2803 return;
2804
2805 assert(mpCol);
2806 ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
2807 aRange.aEnd.SetRow(nRow2);
2808 mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
2809 };
2810};
2811
2812}
2813
2815 const ScRange& rDestRange, const ScMarkData& rMark, InsertDeleteFlags nInsFlag,
2816 ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut,
2817 bool bAsLink, bool bIncludeFiltered, bool bSkipEmptyCells,
2818 const ScRangeList * pDestRanges )
2819{
2820 if (bIsClip)
2821 return;
2822
2823 if (!pClipDoc)
2824 {
2825 OSL_FAIL("CopyFromClip: no ClipDoc");
2826 pClipDoc = ScModule::GetClipDoc();
2827 }
2828
2829 if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
2830 return;
2831
2832 sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc.
2833
2834 NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
2835
2836 SCCOL nAllCol1 = rDestRange.aStart.Col();
2837 SCROW nAllRow1 = rDestRange.aStart.Row();
2838 SCCOL nAllCol2 = rDestRange.aEnd.Col();
2839 SCROW nAllRow2 = rDestRange.aEnd.Row();
2840
2841 SCCOL nXw = 0;
2842 SCROW nYw = 0;
2843 ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
2844 for (SCTAB nTab = 0; nTab < pClipDoc->GetTableCount(); nTab++) // find largest merge overlap
2845 if (pClipDoc->maTabs[nTab]) // all sheets of the clipboard content
2846 {
2847 SCCOL nThisEndX = aClipRange.aEnd.Col();
2848 SCROW nThisEndY = aClipRange.aEnd.Row();
2849 pClipDoc->ExtendMerge( aClipRange.aStart.Col(),
2850 aClipRange.aStart.Row(),
2851 nThisEndX, nThisEndY, nTab );
2852 // only extra value from ExtendMerge
2853 nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() );
2854 nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() );
2855 if ( nThisEndX > nXw )
2856 nXw = nThisEndX;
2857 if ( nThisEndY > nYw )
2858 nYw = nThisEndY;
2859 }
2860
2861 SCCOL nDestAddX;
2862 SCROW nDestAddY;
2863 pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered );
2864 nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX );
2865 nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value
2866
2867 /* Decide which contents to delete before copying. Delete all
2868 contents if nInsFlag contains any real content flag.
2869 #i102056# Notes are pasted from clipboard in a second pass,
2870 together with the special flag InsertDeleteFlags::ADDNOTES that states to not
2871 overwrite/delete existing cells but to insert the notes into
2872 these cells. In this case, just delete old notes from the
2873 destination area. */
2876 nDelFlag |= InsertDeleteFlags::NOTE;
2877 // tdf#141440 - do not delete notes when pasting contents (see InsertDeleteFlags::CONTENTS)
2878 else if ( nInsFlag & (InsertDeleteFlags::CONTENTS & ~InsertDeleteFlags::NOTE) )
2880
2881 if (nInsFlag & InsertDeleteFlags::ATTRIB)
2882 nDelFlag |= InsertDeleteFlags::ATTRIB;
2883
2884 sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipEmptyCells);
2885 std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
2886 aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
2887 aCxt.setDeleteFlag(nDelFlag);
2888
2889 ScRangeList aLocalRangeList;
2890 if (!pDestRanges)
2891 {
2892 aLocalRangeList.push_back( rDestRange);
2893 pDestRanges = &aLocalRangeList;
2894 }
2895
2896 bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
2897
2898 sc::ColumnSpanSet aBroadcastSpans;
2899
2900 SCCOL nClipStartCol = aClipRange.aStart.Col();
2901 SCROW nClipStartRow = aClipRange.aStart.Row();
2902 SCROW nClipEndRow = aClipRange.aEnd.Row();
2903 for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange )
2904 {
2905 const ScRange & rRange = (*pDestRanges)[nRange];
2906 SCCOL nCol1 = rRange.aStart.Col();
2907 SCROW nRow1 = rRange.aStart.Row();
2908 SCCOL nCol2 = rRange.aEnd.Col();
2909 SCROW nRow2 = rRange.aEnd.Row();
2910
2911 aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
2912 DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans); // <- this removes existing formula listeners
2913
2914 if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
2915 continue;
2916
2917 SCCOL nC1 = nCol1;
2918 SCROW nR1 = nRow1;
2919 SCCOL nC2 = nC1 + nXw;
2920 if (nC2 > nCol2)
2921 nC2 = nCol2;
2922 SCROW nR2 = nR1 + nYw;
2923 if (nR2 > nRow2)
2924 nR2 = nRow2;
2925
2926 const SCCOLROW nThreshold = 8192;
2927 bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold));
2928 std::vector< SCTAB > vTables;
2929
2930 if (bPreallocatePattern)
2931 {
2932 for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i)
2933 if (maTabs[i] && rMark.GetTableSelect( i ) )
2934 vTables.push_back( i );
2935 }
2936
2937 do
2938 {
2939 // Pasting is done column-wise, when pasting to a filtered
2940 // area this results in partitioning and we have to
2941 // remember and reset the start row for each column until
2942 // it can be advanced for the next chunk of unfiltered
2943 // rows.
2944 SCROW nSaveClipStartRow = nClipStartRow;
2945 do
2946 {
2947 nClipStartRow = nSaveClipStartRow;
2948 SCCOL nDx = nC1 - nClipStartCol;
2949 SCROW nDy = nR1 - nClipStartRow;
2950 if ( bIncludeFiltered )
2951 {
2953 aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy);
2954 nClipStartRow += nR2 - nR1 + 1;
2955 }
2956 else
2957 {
2958 CopyNonFilteredFromClip(aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow,
2959 nClipEndRow);
2960 }
2961 nC1 = nC2 + 1;
2962 nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2);
2963 } while (nC1 <= nCol2);
2964 if (nClipStartRow > nClipEndRow)
2965 nClipStartRow = aClipRange.aStart.Row();
2966 nC1 = nCol1;
2967 nC2 = nC1 + nXw;
2968 if (nC2 > nCol2)
2969 nC2 = nCol2;
2970
2971 // Preallocate pattern memory once if further chunks are to be pasted.
2972 if (bPreallocatePattern && (nR2+1) <= nRow2)
2973 {
2974 SCROW nR3 = nR2 + 1;
2975 for (SCTAB nTab : vTables)
2976 {
2977 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2978 {
2979 // Pattern count of the first chunk pasted.
2980 SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2);
2981 // If it is only one pattern per chunk and chunks are
2982 // pasted consecutively then it will get its range
2983 // enlarged for each chunk and no further allocation
2984 // happens. For non-consecutive chunks we're out of
2985 // luck in this case.
2986 if (nChunk > 1)
2987 {
2988 SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1);
2989 SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2);
2990 if (nNeeded > nRemain)
2991 {
2992 SCSIZE nCurr = GetPatternCount( nTab, nCol);
2993 ReservePatternCount( nTab, nCol, nCurr + nNeeded);
2994 }
2995 }
2996 }
2997 }
2998 bPreallocatePattern = false;
2999 }
3000
3001 nR1 = nR2 + 1;
3002 nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2);
3003 } while (nR1 <= nRow2);
3004 }
3005
3006 bInsertingFromOtherDoc = false;
3007
3008 if (nInsFlag & InsertDeleteFlags::CONTENTS)
3009 {
3010 for (SCTAB nTab : rMark)
3011 aCxt.setListeningFormulaSpans(nTab, nAllCol1, nAllRow1, nAllCol2, nAllRow2);
3012 }
3013
3014 // Create Listener after everything has been inserted
3016
3017 {
3018 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3019
3020 // Set all formula cells dirty, and collect non-empty non-formula cell
3021 // positions so that we can broadcast on them below.
3022 SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
3023
3024 BroadcastAction aAction(*this);
3025 aBroadcastSpans.executeColumnAction(*this, aAction);
3026 }
3027
3028 if (bResetCut)
3029 pClipDoc->GetClipParam().mbCutMode = false;
3030}
3031
3033 InsertDeleteFlags nInsFlag, ScDocument* pClipDoc,
3034 bool bResetCut, bool bAsLink, bool bIncludeFiltered,
3035 bool bSkipAttrForEmpty)
3036{
3037 if (bIsClip)
3038 return;
3039
3040 if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
3041 // There is nothing in the clip doc to copy.
3042 return;
3043
3044 // Right now, we don't allow pasting into filtered rows, so we don't even handle it here.
3045
3046 sc::AutoCalcSwitch aACSwitch(*this, false); // turn of auto calc temporarily.
3047 NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
3048
3049 const ScRange& aDestRange = rMark.GetMarkArea();
3050
3051 bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
3052
3053 SCCOL nCol1 = rDestPos.Col();
3054 SCROW nRow1 = rDestPos.Row();
3055 ScClipParam& rClipParam = pClipDoc->GetClipParam();
3056
3057 sc::ColumnSpanSet aBroadcastSpans;
3058
3059 if (!bSkipAttrForEmpty)
3060 {
3061 // Do the deletion first.
3062 SCCOL nColSize = rClipParam.getPasteColSize();
3063 SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, bIncludeFiltered);
3064
3065 DeleteArea(nCol1, nRow1, nCol1+nColSize-1, nRow1+nRowSize-1, rMark, InsertDeleteFlags::CONTENTS, false, &aBroadcastSpans);
3066 }
3067
3068 sc::CopyFromClipContext aCxt(*this, nullptr, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty);
3069 std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
3070 aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
3071
3072 for (size_t i = 0, n = rClipParam.maRanges.size(); i < n; ++i)
3073 {
3074 const ScRange & rRange = rClipParam.maRanges[i];
3075
3076 SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
3077 SCCOL nDx = static_cast<SCCOL>(nCol1 - rRange.aStart.Col());
3078 SCROW nDy = static_cast<SCROW>(nRow1 - rRange.aStart.Row());
3079 SCCOL nCol2 = nCol1 + rRange.aEnd.Col() - rRange.aStart.Col();
3080 SCROW nEndRow = nRow1 + nRowCount - 1;
3081 SCROW nFilteredRows = 0;
3082
3083 if (bIncludeFiltered)
3084 {
3085 CopyBlockFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx, nDy);
3086 }
3087 else
3088 {
3089 SCROW nClipStartRow = rRange.aStart.Row();
3090 SCROW nClipEndRow = rRange.aEnd.Row();
3091 nFilteredRows += CopyNonFilteredFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx,
3092 nClipStartRow, nClipEndRow);
3093 nRowCount -= nFilteredRows;
3094 }
3095
3096 switch (rClipParam.meDirection)
3097 {
3098 case ScClipParam::Row:
3099 // Begin row for the next range being pasted.
3100 nRow1 += nRowCount;
3101 break;
3103 nCol1 += rRange.aEnd.Col() - rRange.aStart.Col() + 1;
3104 break;
3105 default:
3106 ;
3107 }
3108 }
3109
3110 bInsertingFromOtherDoc = false;
3111
3112 // Create Listener after everything has been inserted
3113 StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(),
3114 aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag );
3115
3116 {
3117 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3118
3119 // Set formula cells dirty and collect non-formula cells.
3121 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(),
3122 rMark, nInsFlag, aBroadcastSpans);
3123
3124 BroadcastAction aAction(*this);
3125 aBroadcastSpans.executeColumnAction(*this, aAction);
3126 }
3127
3128 if (bResetCut)
3129 pClipDoc->GetClipParam().mbCutMode = false;
3130}
3131
3132void ScDocument::SetClipArea( const ScRange& rArea, bool bCut )
3133{
3134 if (bIsClip)
3135 {
3136 ScClipParam& rClipParam = GetClipParam();
3137 rClipParam.maRanges.RemoveAll();
3138 rClipParam.maRanges.push_back(rArea);
3139 rClipParam.mbCutMode = bCut;
3140 }
3141 else
3142 {
3143 OSL_FAIL("SetClipArea: No Clip");
3144 }
3145}
3146
3147void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, bool bIncludeFiltered)
3148{
3149 if (!bIsClip)
3150 {
3151 OSL_FAIL("GetClipArea: No Clip");
3152 return;
3153 }
3154
3155 ScRangeList& rClipRanges = GetClipParam().maRanges;
3156 if (rClipRanges.empty())
3157 // No clip range. Bail out.
3158 return;
3159
3160 ScRange const & rRange = rClipRanges.front();
3161 SCCOL nStartCol = rRange.aStart.Col();
3162 SCCOL nEndCol = rRange.aEnd.Col();
3163 SCROW nStartRow = rRange.aStart.Row();
3164 SCROW nEndRow = rRange.aEnd.Row();
3165 for ( size_t i = 1, n = rClipRanges.size(); i < n; ++i )
3166 {
3167 ScRange const & rRange2 = rClipRanges[ i ];
3168 if (rRange2.aStart.Col() < nStartCol)
3169 nStartCol = rRange2.aStart.Col();
3170 if (rRange2.aStart.Row() < nStartRow)
3171 nStartRow = rRange2.aStart.Row();
3172 if (rRange2.aEnd.Col() > nEndCol)
3173 nEndCol = rRange2.aEnd.Col();
3174 if (rRange2.aEnd.Row() > nEndRow)
3175 nEndRow = rRange2.aEnd.Row();
3176 }
3177
3178 nClipX = nEndCol - nStartCol;
3179
3180 if ( bIncludeFiltered )
3181 nClipY = nEndRow - nStartRow;
3182 else
3183 {
3184 // count non-filtered rows
3185 // count on first used table in clipboard
3186 SCTAB nCountTab = 0;
3187 while (nCountTab < GetTableCount() && !maTabs[nCountTab])
3188 ++nCountTab;
3189
3190 SCROW nResult = CountNonFilteredRows(nStartRow, nEndRow, nCountTab);
3191
3192 if ( nResult > 0 )
3193 nClipY = nResult - 1;
3194 else
3195 nClipY = 0; // always return at least 1 row
3196 }
3197}
3198
3200{
3201 if (bIsClip)
3202 {
3203 ScRangeList& rClipRanges = GetClipParam().maRanges;
3204 if ( !rClipRanges.empty() )
3205 {
3206 nClipX = rClipRanges.front().aStart.Col();
3207 nClipY = rClipRanges.front().aStart.Row();
3208 }
3209 }
3210 else
3211 {
3212 OSL_FAIL("GetClipStart: No Clip");
3213 }
3214}
3215
3217{
3218 // count on first used table in clipboard
3219 SCTAB nCountTab = 0;
3220 while (nCountTab < GetTableCount() && !maTabs[nCountTab])
3221 ++nCountTab;
3222
3223 ScRangeList& rClipRanges = GetClipParam().maRanges;
3224 if ( rClipRanges.empty() )
3225 return false;
3226
3227 for ( size_t i = 0, n = rClipRanges.size(); i < n; ++i )
3228 {
3229 ScRange & rRange = rClipRanges[ i ];
3230 bool bAnswer = maTabs[nCountTab]->HasFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row());
3231 if (bAnswer)
3232 return true;
3233 }
3234 return false;
3235}
3236
3237void ScDocument::MixDocument( const ScRange& rRange, ScPasteFunc nFunction, bool bSkipEmpty,
3238 ScDocument& rSrcDoc )
3239{
3240 SCTAB nTab1 = rRange.aStart.Tab();
3241 SCTAB nTab2 = rRange.aEnd.Tab();
3242 sc::MixDocContext aCxt(*this);
3243 SCTAB nMinSizeBothTabs = std::min(GetTableCount(), rSrcDoc.GetTableCount());
3244 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
3245 {
3246 ScTable* pTab = FetchTable(i);
3247 const ScTable* pSrcTab = rSrcDoc.FetchTable(i);
3248 if (!pTab || !pSrcTab)
3249 continue;
3250
3251 pTab->MixData(
3252 aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
3253 nFunction, bSkipEmpty, pSrcTab);
3254 }
3255}
3256
3257void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
3258 InsertDeleteFlags nFlags, ScPasteFunc nFunction,
3259 bool bSkipEmpty, bool bAsLink )
3260{
3261 InsertDeleteFlags nDelFlags = nFlags;
3262 if (nDelFlags & InsertDeleteFlags::CONTENTS)
3263 nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
3264
3265 SCTAB nSrcTab = rSrcArea.aStart.Tab();
3266
3267 if (ScTable* pSourceTable = FetchTable(nSrcTab))
3268 {
3269 SCCOL nStartCol = rSrcArea.aStart.Col();
3270 SCROW nStartRow = rSrcArea.aStart.Row();
3271 SCCOL nEndCol = rSrcArea.aEnd.Col();
3272 SCROW nEndRow = rSrcArea.aEnd.Row();
3273 ScDocumentUniquePtr pMixDoc;
3274 bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
3275
3276 bool bOldAutoCalc = GetAutoCalc();
3277 SetAutoCalc( false ); // avoid multiple calculations
3278
3279 sc::CopyToDocContext aCxt(*this);
3280 sc::MixDocContext aMixDocCxt(*this);
3281
3283 for (const SCTAB& i : rMark)
3284 {
3285 if (i >= nCount)
3286 break;
3287 if (i != nSrcTab && maTabs[i])
3288 {
3289 if (bDoMix)
3290 {
3291 if (!pMixDoc)
3292 {
3293 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
3294 pMixDoc->InitUndo( *this, i, i );
3295 }
3296 else
3297 pMixDoc->AddUndoTab( i, i );
3298
3299 // context used for copying content to the temporary mix document.
3300 sc::CopyToDocContext aMixCxt(*pMixDoc);
3301 maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3302 InsertDeleteFlags::CONTENTS, false, pMixDoc->maTabs[i].get(),
3303 /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true,
3304 /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3305 }
3306 maTabs[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags);
3307 pSourceTable->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3308 nFlags, false, maTabs[i].get(), nullptr, bAsLink,
3309 /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3310
3311 if (bDoMix)
3312 maTabs[i]->MixData(aMixDocCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3313 nFunction, bSkipEmpty, pMixDoc->maTabs[i].get() );
3314 }
3315 }
3316
3317 SetAutoCalc( bOldAutoCalc );
3318 }
3319 else
3320 {
3321 OSL_FAIL("wrong table");
3322 }
3323}
3324
3325void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
3326 InsertDeleteFlags nFlags, ScPasteFunc nFunction,
3327 bool bSkipEmpty, bool bAsLink )
3328{
3329 InsertDeleteFlags nDelFlags = nFlags;
3330 if (nDelFlags & InsertDeleteFlags::CONTENTS)
3331 nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
3332
3333 if (ScTable* pSourceTable = FetchTable(nSrcTab))
3334 {
3335 ScDocumentUniquePtr pMixDoc;
3336 bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
3337
3338 bool bOldAutoCalc = GetAutoCalc();
3339 SetAutoCalc( false ); // avoid multiple calculations
3340
3341 const ScRange& aArea = rMark.GetMultiMarkArea();
3342 SCCOL nStartCol = aArea.aStart.Col();
3343 SCROW nStartRow = aArea.aStart.Row();
3344 SCCOL nEndCol = aArea.aEnd.Col();
3345 SCROW nEndRow = aArea.aEnd.Row();
3346
3347 sc::CopyToDocContext aCxt(*this);
3348 sc::MixDocContext aMixDocCxt(*this);
3350 for (const SCTAB& i : rMark)
3351 {
3352 if (i >= nCount)
3353 break;
3354 if ( i != nSrcTab && maTabs[i] )
3355 {
3356 if (bDoMix)
3357 {
3358 if (!pMixDoc)
3359 {
3360 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
3361 pMixDoc->InitUndo( *this, i, i );
3362 }
3363 else
3364 pMixDoc->AddUndoTab( i, i );
3365
3366 sc::CopyToDocContext aMixCxt(*pMixDoc);
3367 maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3368 InsertDeleteFlags::CONTENTS, true, pMixDoc->maTabs[i].get(), &rMark,
3369 /*bAsLink*/false, /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false,
3370 /*bCopyCaptions*/true );
3371 }
3372
3373 maTabs[i]->DeleteSelection( nDelFlags, rMark );
3374 pSourceTable->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
3375 nFlags, true, maTabs[i].get(), &rMark, bAsLink,
3376 /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
3377
3378 if (bDoMix)
3379 maTabs[i]->MixMarked(aMixDocCxt, rMark, nFunction, bSkipEmpty, pMixDoc->maTabs[i].get());
3380 }
3381 }
3382
3383 SetAutoCalc( bOldAutoCalc );
3384 }
3385 else
3386 {
3387 OSL_FAIL("wrong table");
3388 }
3389}
3390
3391bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString,
3392 const ScSetStringParam* pParam )
3393{
3394 ScTable* pTab = FetchTable(nTab);
3395 if (!pTab)
3396 return false;
3397
3398 const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(nCol, nRow);
3399 if (pCurCellFormula && pCurCellFormula->IsShared())
3400 {
3401 // In case setting this string affects an existing formula group, end
3402 // its listening to purge then empty cell broadcasters. Affected
3403 // remaining split group listeners will be set up again via
3404 // ScColumn::DetachFormulaCell() and
3405 // ScColumn::StartListeningUnshared().
3406
3407 sc::EndListeningContext aCxt(*this);
3408 ScAddress aPos(nCol, nRow, nTab);
3409 EndListeningIntersectedGroup(aCxt, aPos, nullptr);
3411 }
3412
3413 return pTab->SetString(nCol, nRow, nTab, rString, pParam);
3414}
3415
3417 const ScAddress& rPos, const OUString& rString, const ScSetStringParam* pParam )
3418{
3419 return SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rString, pParam);
3420}
3421
3422bool ScDocument::SetEditText( const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText )
3423{
3424 if (ScTable* pTable = FetchTable(rPos.Tab()))
3425 return pTable->SetEditText(rPos.Col(), rPos.Row(), std::move(pEditText));
3426 return false;
3427}
3428
3429void ScDocument::SetEditText( const ScAddress& rPos, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
3430{
3431 if (ScTable* pTable = FetchTable(rPos.Tab()))
3432 pTable->SetEditText(rPos.Col(), rPos.Row(), rEditText, pEditPool);
3433}
3434
3435void ScDocument::SetEditText( const ScAddress& rPos, const OUString& rStr )
3436{
3437 if (ScTable* pTable = FetchTable(rPos.Tab()))
3438 {
3439 ScFieldEditEngine& rEngine = GetEditEngine();
3440 rEngine.SetTextCurrentDefaults(rStr);
3441 pTable->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
3442 }
3443}
3444
3446{
3447 if (const ScTable* pTable = FetchTable(rRange.aStart.Tab()))
3448 return pTable->GetFirstEditTextRow(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
3449 return -1;
3450}
3451
3452void ScDocument::SetTextCell( const ScAddress& rPos, const OUString& rStr )
3453{
3454 if (ScTable* pTable = FetchTable(rPos.Tab()))
3455 {
3456 if (ScStringUtil::isMultiline(rStr))
3457 {
3458 ScFieldEditEngine& rEngine = GetEditEngine();
3459 rEngine.SetTextCurrentDefaults(rStr);
3460 pTable->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
3461 }
3462 else
3463 {
3464 ScSetStringParam aParam;
3465 aParam.setTextInput();
3466 pTable->SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rStr, &aParam);
3467 }
3468 }
3469}
3470
3472{
3473 if (ScTable* pTable = FetchTable(rPos.Tab()))
3474 pTable->SetEmptyCell(rPos.Col(), rPos.Row());
3475}
3476
3477void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal )
3478{
3479 SetValue(ScAddress(nCol, nRow, nTab), rVal);
3480}
3481
3482void ScDocument::SetValue( const ScAddress& rPos, double fVal )
3483{
3484 ScTable* pTab = FetchTable(rPos.Tab());
3485 if (!pTab)
3486 return;
3487
3488 const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(rPos.Col(), rPos.Row());
3489 if (pCurCellFormula && pCurCellFormula->IsShared())
3490 {
3491 // In case setting this value affects an existing formula group, end
3492 // its listening to purge then empty cell broadcasters. Affected
3493 // remaining split group listeners will be set up again via
3494 // ScColumn::DetachFormulaCell() and
3495 // ScColumn::StartListeningUnshared().
3496
3497 sc::EndListeningContext aCxt(*this);
3498 EndListeningIntersectedGroup(aCxt, rPos, nullptr);
3500 }
3501
3502 pTab->SetValue(rPos.Col(), rPos.Row(), fVal);
3503}
3504
3505OUString ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext* pContext ) const
3506{
3507 if (const ScTable* pTable = FetchTable(nTab))
3508 return pTable->GetString(nCol, nRow, pContext);
3509 return OUString();
3510}
3511
3512OUString ScDocument::GetString( const ScAddress& rPos, const ScInterpreterContext* pContext ) const
3513{
3514 if (const ScTable* pTable = FetchTable(rPos.Tab()))
3515 return pTable->GetString(rPos.Col(), rPos.Row(), pContext);
3516 return OUString();
3517}
3518
3520{
3521 if (ScTable* pTable = FetchTable(rPos.Tab()))
3522 return pTable->GetValueCell(rPos.Col(), rPos.Row());
3523 return nullptr;
3524}
3525
3527{
3528 if (const ScTable* pTable = FetchTable(rPos.Tab()))
3529 return pTable->GetSharedString(rPos.Col(), rPos.Row());
3530 return svl::SharedString();
3531}
3532
3533std::shared_ptr<sc::FormulaGroupContext>& ScDocument::GetFormulaGroupContext()
3534{
3536 if (!mpFormulaGroupCxt)
3537 mpFormulaGroupCxt = std::make_shared<sc::FormulaGroupContext>();
3538
3539 return mpFormulaGroupCxt;
3540}
3541
3543{
3546 mpFormulaGroupCxt.reset();
3547}
3548
3549OUString ScDocument::GetInputString(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bForceSystemLocale ) const
3550{
3551 if (const ScTable* pTable = FetchTable(nTab))
3552 return pTable->GetInputString(nCol, nRow, bForceSystemLocale);
3553 else
3554 return OUString();
3555}
3556
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.clear();
3567 return FormulaError::NONE;
3568 }
3569
3570 FormulaError nErr = FormulaError::NONE;
3571 OUString aStr;
3572 SvNumberFormatter* pFormatter = GetFormatTable();
3573 switch (aCell.getType())
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.getFormula();
3582 nErr = pFCell->GetErrCode();
3583 if (pFCell->IsValue())
3584 {
3585 double fVal = pFCell->GetValue();
3586 sal_uInt32 nIndex = pFormatter->GetStandardFormat(
3587 SvNumFormatType::NUMBER,
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.getDouble();
3598 sal_uInt32 nIndex = pFormatter->GetStandardFormat(
3599 SvNumFormatType::NUMBER,
3601 pFormatter->GetInputLineString(fVal, nIndex, aStr);
3602 }
3603 break;
3604 default:
3605 ;
3606 }
3607
3608 rString = aStr;
3609 return nErr;
3610}
3611
3613{
3614 SCTAB nTab = rPos.Tab();
3615 if (const ScTable* pTable = FetchTable(nTab))
3616 return pTable->GetEditText(rPos.Col(), rPos.Row());
3617 return nullptr;
3618}
3619
3621{
3622 if (ScTable* pTable = FetchTable(rPos.Tab()))
3623 return pTable->RemoveEditTextCharAttribs(rPos.Col(), rPos.Row(), rAttr);
3624}
3625
3626double ScDocument::GetValue( const ScAddress& rPos ) const
3627{
3628 SCTAB nTab = rPos.Tab();
3629 if (const ScTable* pTable = FetchTable(nTab))
3630 return pTable->GetValue(rPos.Col(), rPos.Row());
3631 return 0.0;
3632}
3633
3634double ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3635{
3636 ScAddress aAdr(nCol, nRow, nTab);
3637 return GetValue(aAdr);
3638}
3639
3640sal_uInt32 ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3641{
3642 if (const ScTable* pTable = FetchTable(nTab))
3643 return pTable->GetNumberFormat(nCol, nRow);
3644 return 0;
3645}
3646
3647sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const
3648{
3649 SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
3650 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
3651 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
3652
3653 if (!HasTable(nTab1) || !HasTable(nTab2))
3654 return 0;
3655
3656 sal_uInt32 nFormat = 0;
3657 bool bFirstItem = true;
3658 for (SCTAB nTab = nTab1; nTab <= nTab2 && nTab < GetTableCount() ; ++nTab)
3659 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
3660 {
3661 sal_uInt32 nThisFormat = maTabs[nTab]->GetNumberFormat(nCol, nRow1, nRow2);
3662 if (bFirstItem)
3663 {
3664 nFormat = nThisFormat;
3665 bFirstItem = false;
3666 }
3667 else if (nThisFormat != nFormat)
3668 return 0;
3669 }
3670
3671 return nFormat;
3672}
3673
3674sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
3675{
3676 SCTAB nTab = rPos.Tab();
3677 if (const ScTable* pTable = FetchTable(nTab))
3678 return pTable->GetNumberFormat( rContext, rPos );
3679 return 0;
3680}
3681
3682void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat )
3683{
3685 SCTAB nTab = rPos.Tab();
3686 if (ScTable* pTable = FetchTable(nTab))
3687 pTable->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat);
3688}
3689
3691 const ScAddress& rPos ) const
3692{
3693 SCTAB nTab = rPos.Tab();
3694 if (nTab < GetTableCount() && maTabs[nTab])
3695 {
3696 nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos );
3697 nType = rContext.GetNumberFormatType( nIndex );
3698 }
3699 else
3700 {
3701 nType = SvNumFormatType::UNDEFINED;
3702 nIndex = 0;
3703 }
3704}
3705
3706OUString ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3707{
3708 if (const ScTable* pTable = FetchTable(nTab))
3709 return pTable->GetFormula(nCol, nRow);
3710
3711 return OUString();
3712}
3713
3715{
3716 if (const ScTable* pTable = FetchTable(rPos.Tab()))
3717 return pTable->GetFormulaCell(rPos.Col(), rPos.Row());
3718 return nullptr;
3719}
3720
3722{
3723 if (ScTable* pTable = FetchTable(rPos.Tab()))
3724 return pTable->GetFormulaCell(rPos.Col(), rPos.Row());
3725 return nullptr;
3726}
3727
3729{
3730 SCTAB nTab = rPos.Tab();
3731 if (const ScTable* pTable = FetchTable(nTab))
3732 return pTable->GetCellType(rPos);
3733 return CELLTYPE_NONE;
3734}
3735
3737{
3738 if (const ScTable* pTable = FetchTable(nTab))
3739 return pTable->GetCellType( nCol, nRow );
3740 return CELLTYPE_NONE;
3741}
3742
3743bool ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3744{
3745 if (const ScTable* pTable = FetchTable(nTab) ; pTable && nCol < pTable->GetAllocatedColumnsCount())
3746 return pTable->HasStringData(nCol, nRow);
3747 return false;
3748}
3749
3750bool ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3751{
3752 if (const ScTable* pTable = FetchTable(nTab) ; pTable && nCol < pTable->GetAllocatedColumnsCount())
3753 return pTable->HasValueData( nCol, nRow );
3754 return false;
3755}
3756
3757bool ScDocument::HasValueData( const ScAddress& rPos ) const
3758{
3759 return HasValueData(rPos.Col(), rPos.Row(), rPos.Tab());
3760}
3761
3762bool ScDocument::HasStringCells( const ScRange& rRange ) const
3763{
3764 // true, if String- or Edit cells in range
3765
3766 SCCOL nStartCol = rRange.aStart.Col();
3767 SCROW nStartRow = rRange.aStart.Row();
3768 SCTAB nStartTab = rRange.aStart.Tab();
3769 SCCOL nEndCol = rRange.aEnd.Col();
3770 SCROW nEndRow = rRange.aEnd.Row();
3771 SCTAB nEndTab = rRange.aEnd.Tab();
3772
3773 for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < GetTableCount(); nTab++)
3774 {
3775 if ( maTabs[nTab] && maTabs[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) )
3776 return true;
3777 }
3778 return false;
3779}
3780
3781bool ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3782{
3783 sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
3784 if( nValidation )
3785 {
3786 const ScValidationData* pData = GetValidationEntry( nValidation );
3787 if( pData && pData->HasSelectionList() )
3788 return true;
3789 }
3790 return HasStringCells( ScRange( nCol, 0, nTab, nCol, MaxRow(), nTab ) );
3791}
3792
3793bool ScDocument::HasValidationData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
3794{
3795 sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
3796 if( nValidation )
3797 {
3798 const ScValidationData* pData = GetValidationEntry( nValidation );
3799 if( pData && pData->GetDataMode() != ScValidationMode::SC_VALID_ANY )
3800 return true;
3801 }
3802 return false;
3803}
3804
3806{
3807 bool bOldAutoCalc = GetAutoCalc();
3808 bAutoCalc = false; // no multiple calculations
3809
3810 for (const auto& a : maTabs)
3811 {
3812 if (a)
3813 a->CheckVectorizationState();
3814 }
3815
3816 SetAutoCalc(bOldAutoCalc);
3817}
3818
3820{
3821 bool bOldAutoCalc = GetAutoCalc();
3822 bAutoCalc = false; // no multiple calculations
3823 { // scope for bulk broadcast
3824 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3825 for (const auto& a : maTabs)
3826 {
3827 if (a)
3828 a->SetAllFormulasDirty(rCxt);
3829 }
3830 }
3831
3832 // Although Charts are also set to dirty in Tracking without AutoCalc
3833 // if all formulas are dirty, the charts can no longer be caught
3834 // (#45205#) - that is why all Charts have to be explicitly handled again
3836 pChartListenerCollection->SetDirty();
3837
3838 SetAutoCalc( bOldAutoCalc );
3839}
3840
3841void ScDocument::SetDirty( const ScRange& rRange, bool bIncludeEmptyCells )
3842{
3843 bool bOldAutoCalc = GetAutoCalc();
3844 bAutoCalc = false; // no multiple calculations
3845 { // scope for bulk broadcast
3846 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
3847 SCTAB nTab2 = rRange.aEnd.Tab();
3848 for (SCTAB i = rRange.aStart.Tab(); i <= nTab2 && i < GetTableCount(); i++)
3849 if (maTabs[i]) maTabs[i]->SetDirty( rRange,
3851
3852 /* TODO: this now also notifies conditional formatting and does a UNO
3853 * broadcast, which wasn't done here before. Is that an actually
3854 * desired side effect, or should we come up with a method that
3855 * doesn't? */
3856 if (bIncludeEmptyCells)
3857 BroadcastCells( rRange, SfxHintId::ScDataChanged, false);
3858 }
3859 SetAutoCalc( bOldAutoCalc );
3860}
3861
3863{
3864 bool bOldAutoCalc = GetAutoCalc();
3865 bAutoCalc = false; // no multiple recalculation
3866 SCTAB nTab2 = rRange.aEnd.Tab();
3867 for (SCTAB i = rRange.aStart.Tab(); i <= nTab2 && i < GetTableCount(); i++)
3868 if (maTabs[i]) maTabs[i]->SetTableOpDirty( rRange );
3869 SetAutoCalc( bOldAutoCalc );
3870}
3871
3873{
3874 if (!GetAutoCalc())
3875 return;
3876
3878
3879 for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
3880 {
3881 const ScRange& rRange = rRanges[nPos];
3882 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
3883 {
3884 ScTable* pTab = FetchTable(nTab);
3885 if (!pTab)
3886 return;
3887
3888 pTab->InterpretDirtyCells(
3889 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
3890 }
3891 }
3892
3894 mpFormulaGroupCxt.reset();
3895}
3896
3898{
3899 bool allInterpreted = true;
3900 for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
3901 {
3902 const ScRange& rRange = rRanges[nPos];
3903 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
3904 {
3905 ScTable* pTab = FetchTable(nTab);
3906 if (!pTab)
3907 break;
3908
3909 if( !pTab->InterpretCellsIfNeeded(
3910 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()))
3911 {
3912 allInterpreted = false;
3913 }
3914 }
3915 }
3916 return allInterpreted;
3917}
3918
3920{
3921 if (m_TableOpList.empty())
3922 return;
3923
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
3940{
3942 ClearLookupCaches(); // Ensure we don't deliver zombie data.
3943 sc::AutoCalcSwitch aSwitch(*this, true);
3944 for (const auto& a : maTabs)
3945 {
3946 if (a)
3947 a->SetDirtyVar();
3948 }
3949 for (const auto& a : maTabs)
3950 {
3951 if (a)
3952 a->CalcAll();
3953 }
3955
3956 // In eternal hard recalc state caches were not added as listeners,
3957 // invalidate them so the next non-CalcAll() normal lookup will not be
3958 // presented with outdated data.
3961}
3962
3964{
3965 sc::CompileFormulaContext aCxt(*this);
3966 for (const auto& a : maTabs)
3967 {
3968 if (a)
3969 a->CompileAll(aCxt);
3970 }
3971
3972 sc::SetFormulaDirtyContext aFormulaDirtyCxt;
3973 SetAllFormulasDirty(aFormulaDirtyCxt);
3974}
3975
3977{
3978 bool bOldAutoCalc = GetAutoCalc();
3979 SetAutoCalc( false );
3980 ScProgress aProgress( GetDocumentShell(), ScResId(
3981 STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount(), true );
3982
3983 sc::CompileFormulaContext aCxt(*this);
3984
3985 // set AutoNameCache to speed up automatic name lookup
3986 OSL_ENSURE( !pAutoNameCache, "AutoNameCache already set" );
3987 pAutoNameCache.reset( new ScAutoNameCache( *this ) );
3988
3989 if (pRangeName)
3990 pRangeName->CompileUnresolvedXML(aCxt);
3991
3992 std::for_each(maTabs.begin(), maTabs.end(),
3993 [&](ScTableUniquePtr & pTab)
3994 {
3995 if (pTab)
3996 pTab->CompileXML(aCxt, aProgress);
3997 }
3998 );
4000
4001 pAutoNameCache.reset(); // valid only during CompileXML, where cell contents don't change
4002
4003 if ( pValidationList )
4004 {
4006 pValidationList->CompileXML();
4007 }
4008
4009 // Track all formula cells that were appended to the FormulaTrack during
4010 // import or CompileXML().
4011 TrackFormulas();
4012
4013 SetAutoCalc( bOldAutoCalc );
4014}
4015
4017{
4018 bool bCompiled = false;
4019 sc::CompileFormulaContext aCxt(*this);
4020 for (const auto& pTab : maTabs)
4021 {
4022 if (pTab && pTab->CompileErrorCells(aCxt, nErrCode))
4023 bCompiled = true;
4024 }
4025
4026 return bCompiled;
4027}
4028
4029void ScDocument::CalcAfterLoad( bool bStartListening )
4030{
4031 if (bIsClip) // Excel data is loaded from the Clipboard to a Clip-Doc
4032 return; // the calculation is then only performed when inserting into the real document
4033
4034 bCalcingAfterLoad = true;
4035 sc::CompileFormulaContext aCxt(*this);
4036 {
4037 for (const auto& pTable : maTabs)
4038 {
4039 if (pTable)
4040 pTable->CalcAfterLoad(aCxt, bStartListening);
4041 }
4042 for (const auto& pTable : maTabs)
4043 {
4044 if (pTable)
4045 pTable->SetDirtyAfterLoad();
4046 }
4047 }
4048 bCalcingAfterLoad = false;
4049
4050 SetDetectiveDirty(false); // No real changes yet
4051
4052 // #i112436# If formula cells are already dirty, they don't broadcast further changes.
4053 // So the source ranges of charts must be interpreted even if they are not visible,
4054 // similar to ScMyShapeResizer::CreateChartListener for loading own files (i104899).
4056 {
4057 const ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners();
4058 for (auto const& it : rListeners)
4059 {
4060 const ScChartListener *const p = it.second.get();
4061 InterpretDirtyCells(*p->GetRangeList());
4062 }
4063 }
4064}
4065
4067{
4068 SCTAB nTab = rPos.Tab();
4069 if (const ScTable* pTable = FetchTable(nTab))
4070 return pTable->GetErrCode( rPos );
4071 return FormulaError::NONE;
4072}
4073
4075{
4077 SCTAB nTab1 = rRange.aStart.Tab();
4078 SCTAB nTab2 = rRange.aEnd.Tab();
4079 for (SCTAB nTab = nTab1; nTab1 <= nTab2 && nTab < nTabSize; ++nTab)
4080 if (maTabs[nTab])
4081 maTabs[nTab]->ResetChanged(rRange);
4082}
4083
4084// Column widths / Row heights --------------------------------------
4085
4086void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
4087{
4088 if (ScTable* pTable = FetchTable(nTab))
4089 pTable->SetColWidth(nCol, nNewWidth);
4090}
4091
4092void ScDocument::SetColWidthOnly( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
4093{
4094 if (ScTable* pTable = FetchTable(nTab))
4095 pTable->SetColWidthOnly(nCol, nNewWidth);
4096}
4097
4098void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, sal_uInt16 nNewHeight )
4099{
4100 if (ScTable* pTable = FetchTable(nTab))
4101 pTable->SetRowHeight(nRow, nNewHeight);
4102}
4103
4104void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
4105{
4106 if (ScTable* pTable = FetchTable(nTab))
4107 pTable->SetRowHeightRange(nStartRow, nEndRow, nNewHeight, 1.0, true);
4108}
4109
4110void ScDocument::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
4111{
4112 if (ScTable* pTable = FetchTable(nTab))
4113 pTable->SetRowHeightOnly( nStartRow, nEndRow, nNewHeight );
4114}
4115
4116void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bManual )
4117{
4118 if (ScTable* pTable = FetchTable(nTab))
4119 pTable->SetManualHeight( nStartRow, nEndRow, bManual );
4120}
4121
4122sal_uInt16 ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
4123{
4124 if (const ScTable* pTable = FetchTable(nTab))
4125 return pTable->GetColWidth( nCol, bHiddenAsZero );
4126 OSL_FAIL("wrong table number");
4127 return 0;
4128}
4129
4130tools::Long ScDocument::GetColWidth( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab ) const
4131{
4132 if (const ScTable* pTable = FetchTable(nTab))
4133 return pTable->GetColWidth(nStartCol, nEndCol);
4134 return 0;
4135}
4136
4137sal_uInt16 ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const
4138{
4139 if (const ScTable* pTable = FetchTable(nTab))
4140 return pTable->GetOriginalWidth( nCol );
4141 OSL_FAIL("wrong table number");
4142 return 0;
4143}
4144
4145sal_uInt16 ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const
4146{
4147 if (const ScTable* pTable = FetchTable(nTab))
4148 return pTable->GetCommonWidth( nEndCol );
4149 OSL_FAIL("Wrong table number");
4150 return 0;
4151}
4152
4153sal_uInt16 ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const
4154{
4155 if (const ScTable* pTable = FetchTable(nTab))
4156 return pTable->GetOriginalHeight( nRow );
4157 OSL_FAIL("Wrong table number");
4158 return 0;
4159}
4160
4161sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
4162{
4163 if (const ScTable* pTable = FetchTable(nTab))
4164 return pTable->GetRowHeight( nRow, nullptr, nullptr, bHiddenAsZero );
4165 OSL_FAIL("Wrong sheet number");
4166 return 0;
4167}
4168
4169sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const
4170{
4171 if (const ScTable* pTable = FetchTable(nTab))
4172 return pTable->GetRowHeight( nRow, pStartRow, pEndRow, bHiddenAsZero );
4173 OSL_FAIL("Wrong sheet number");
4174 return 0;
4175}
4176
4177tools::Long ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
4178{
4179 if (nStartRow == nEndRow)
4180 return GetRowHeight( nStartRow, nTab, bHiddenAsZero ); // faster for a single row
4181
4182 // check bounds because this method replaces former for(i=start;i<=end;++i) loops
4183 if (nStartRow > nEndRow)
4184 return 0;
4185
4186 if (const ScTable* pTable = FetchTable(nTab))
4187 return pTable->GetRowHeight( nStartRow, nEndRow, bHiddenAsZero );
4188
4189 OSL_FAIL("wrong sheet number");
4190 return 0;
4191}
4192
4194{
4195 return maTabs[nTab]->GetRowForHeight(nHeight);
4196}
4197
4199 SCTAB nTab, double fScale ) const
4200{
4201 // faster for a single row
4202 if (nStartRow == nEndRow)
4203 return static_cast<tools::Long>(GetRowHeight( nStartRow, nTab) * fScale);
4204
4205 // check bounds because this method replaces former for(i=start;i<=end;++i) loops
4206 if (nStartRow > nEndRow)
4207 return 0;
4208
4209 if (const ScTable* pTable = FetchTable(nTab))
4210 return pTable->GetScaledRowHeight( nStartRow, nEndRow, fScale);
4211
4212 OSL_FAIL("wrong sheet number");
4213 return 0;
4214}
4215
4217{
4218 if (const ScTable* pTable = FetchTable(nTab))
4219 return pTable->GetHiddenRowCount( nRow );
4220 OSL_FAIL("wrong table number");
4221 return 0;
4222}
4223
4224tools::Long ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
4225{
4226 if (const ScTable* pTable = FetchTable(nTab))
4227 return pTable->GetColOffset( nCol, bHiddenAsZero );
4228 OSL_FAIL("wrong table number");
4229 return 0;
4230}
4231
4232tools::Long ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
4233{
4234 if (const ScTable* pTable = FetchTable(nTab))
4235 return pTable->GetRowOffset( nRow, bHiddenAsZero );
4236 OSL_FAIL("wrong table number");
4237 return 0;
4238}
4239
4241 double nPPTX, double nPPTY,
4242 const Fraction& rZoomX, const Fraction& rZoomY,
4243 bool bFormula, const ScMarkData* pMarkData,
4244 const ScColWidthParam* pParam )
4245{
4246 if (ScTable* pTable = FetchTable(nTab))
4247 return pTable->GetOptimalColWidth(nCol, pDev, nPPTX, nPPTY, rZoomX,
4248 rZoomY, bFormula, pMarkData, pParam);
4249 OSL_FAIL("wrong table number");
4250 return 0;
4251}
4252
4254 OutputDevice* pDev,
4255 double nPPTX, double nPPTY,
4256 const Fraction& rZoomX, const Fraction& rZoomY,
4257 bool bWidth, bool bTotalSize, bool bInPrintTwips )
4258{
4259 if (ScTable* pTable = FetchTable(nTab))
4260 return pTable->GetNeededSize(nCol, nRow, pDev, nPPTX, nPPTY,
4261 rZoomX, rZoomY, bWidth, bTotalSize,
4262 bInPrintTwips);
4263 OSL_FAIL("wrong table number");
4264 return 0;
4265}
4266
4267bool ScDocument::SetOptimalHeight( sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bApi )
4268{
4269 if (ScTable* pTable = FetchTable(nTab))
4270 return pTable->SetOptimalHeight(rCxt, nStartRow, nEndRow, bApi);
4271 return false;
4272}
4273
4275{
4276 // one progress across all (selected) sheets
4277
4278 sal_uInt64 nCellCount = 0;
4279 for (SCTAB nTab = 0; nTab < GetTableCount(); nTab++)
4280 if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
4281 nCellCount += maTabs[nTab]->GetWeightedCount();
4282
4283 ScProgress aProgress( GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true );
4284
4285 sal_uInt64 nProgressStart = 0;
4286 for (SCTAB nTab = 0; nTab < GetTableCount(); nTab++)
4287 if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
4288 {
4289 maTabs[nTab]->SetOptimalHeightOnly(rCxt, 0, MaxRow(), &aProgress, nProgressStart);
4290 maTabs[nTab]->SetDrawPageSize();
4291 nProgressStart += maTabs[nTab]->GetWeightedCount();
4292 }
4293}
4294
4295// Column/Row - Flags ----------------------------------------------
4296
4297void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, bool bShow)
4298{
4299 if (ScTable* pTable = FetchTable(nTab))
4300 pTable->ShowCol(nCol, bShow);
4301}
4302
4303void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, bool bShow)
4304{
4305 if (ScTable* pTable = FetchTable(nTab))
4306 pTable->ShowRow(nRow, bShow);
4307}
4308
4309void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, bool bShow)
4310{
4311 if (ScTable* pTable = FetchTable(nTab))
4312 pTable->ShowRows( nRow1, nRow2, bShow );
4313}
4314
4315void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, CRFlags nNewFlags )
4316{
4317 if (ScTable* pTable = FetchTable(nTab))
4318 pTable->SetRowFlags( nRow, nNewFlags );
4319}
4320
4321void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, CRFlags nNewFlags )
4322{
4323 if (ScTable* pTable = FetchTable(nTab))
4324 pTable->SetRowFlags( nStartRow, nEndRow, nNewFlags );
4325}
4326
4328{
4329 if (const ScTable* pTable = FetchTable(nTab))
4330 return pTable->GetColFlags( nCol );
4331 OSL_FAIL("wrong table number");
4332 return CRFlags::NONE;
4333}
4334
4336{
4337 if (const ScTable* pTable = FetchTable(nTab))
4338 return pTable->GetRowFlags( nRow );
4339 OSL_FAIL("wrong table number");
4340 return CRFlags::NONE;
4341}
4342
4343void ScDocument::GetAllRowBreaks(set<SCROW>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
4344{
4345 if (const ScTable* pTable = FetchTable(nTab))
4346 pTable->GetAllRowBreaks(rBreaks, bPage, bManual);
4347}
4348
4349void ScDocument::GetAllColBreaks(set<SCCOL>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
4350{
4351 if (const ScTable* pTable = FetchTable(nTab))
4352 pTable->GetAllColBreaks(rBreaks, bPage, bManual);
4353}
4354
4356{
4358 if (const ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
4359 {
4360 if (pTable->HasRowPageBreak(nRow))
4362
4363 if (pTable->HasRowManualBreak(nRow))
4365 }
4366 return nType;
4367}
4368
4370{
4372
4373 if (const ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
4374 {
4375 if (pTable->HasColPageBreak(nCol))
4377
4378 if (pTable->HasColManualBreak(nCol))
4380 }
4381 return nType;
4382}
4383
4384void ScDocument::SetRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
4385{
4386 if (ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
4387 pTable->SetRowBreak(nRow, bPage, bManual);
4388}
4389
4390void ScDocument::SetColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
4391{
4392 if (ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
4393 pTable->SetColBreak(nCol, bPage, bManual);
4394}
4395
4396void ScDocument::RemoveRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
4397{
4398 if (ScTable* pTable = FetchTable(nTab); pTable && ValidRow(nRow))
4399 pTable->RemoveRowBreak(nRow, bPage, bManual);
4400}
4401
4402void ScDocument::RemoveColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
4403{
4404 if (ScTable* pTable = FetchTable(nTab); pTable && ValidCol(nCol))
4405 pTable->RemoveColBreak(nCol, bPage, bManual);
4406}
4407
4408Sequence<TablePageBreakData> ScDocument::GetRowBreakData(SCTAB nTab) const
4409{
4410 if (const ScTable* pTable = FetchTable(nTab))
4411 return pTable->GetRowBreakData();
4412
4413 return Sequence<TablePageBreakData>();
4414}
4415
4416bool ScDocument::RowHidden(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
4417{
4418 if (const ScTable* pTable = FetchTable(nTab))
4419 return pTable->RowHidden(nRow, pFirstRow, pLastRow);
4420 return false;
4421}
4422
4423bool ScDocument::HasHiddenRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4424{
4425 if (const ScTable* pTable = FetchTable(nTab))
4426 return pTable->HasHiddenRows(nStartRow, nEndRow);
4427 return false;
4428}
4429
4430bool ScDocument::ColHidden(SCCOL nCol, SCTAB nTab, SCCOL* pFirstCol, SCCOL* pLastCol) const
4431{
4432 if (const ScTable* pTable = FetchTable(nTab))
4433 return pTable->ColHidden(nCol, pFirstCol, pLastCol);
4434
4435 if (pFirstCol)
4436 *pFirstCol = nCol;
4437 if (pLastCol)
4438 *pLastCol = nCol;
4439 return false;
4440}
4441
4442void ScDocument::SetRowHidden(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHidden)
4443{
4444 if (ScTable* pTable = FetchTable(nTab))
4445 pTable->SetRowHidden(nStartRow, nEndRow, bHidden);
4446}
4447
4448void ScDocument::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bHidden)
4449{
4450 if (ScTable* pTable = FetchTable(nTab))
4451 pTable->SetColHidden(nStartCol, nEndCol, bHidden);
4452}
4453
4454SCROW ScDocument::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4455{
4456 if (const ScTable* pTable = FetchTable(nTab))
4457 return pTable->FirstVisibleRow(nStartRow, nEndRow);
4458 return 0;
4459}
4460
4461SCROW ScDocument::LastVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4462{
4463 if (const ScTable* pTable = FetchTable(nTab))
4464 return pTable->LastVisibleRow(nStartRow, nEndRow);
4465 return ::std::numeric_limits<SCROW>::max();
4466}
4467
4468SCROW ScDocument::CountVisibleRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4469{
4470 if (const ScTable* pTable = FetchTable(nTab))
4471 return pTable->CountVisibleRows(nStartRow, nEndRow);
4472 return 0;
4473}
4474
4475bool ScDocument::RowFiltered(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
4476{
4477 if (const ScTable* pTable = FetchTable(nTab))
4478 return pTable->RowFiltered(nRow, pFirstRow, pLastRow);
4479 return false;
4480}
4481
4482bool ScDocument::HasFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
4483{
4484 if (const ScTable* pTable = FetchTable(nTab))
4485 return pTable->HasFilteredRows(nStartRow, nEndRow);
4486 return false;
4487}
4488
4490{
4491 if (const ScTable* pTable = FetchTable(nTab))
4492 return pTable->ColFiltered(nCol);
4493 return false;
4494}
4495
4496void ScDocument::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bFiltered)
4497{
4498 if (ScTable* pTable = FetchTable(nTab))
4499 pTable->SetRowFiltered(nStartRow, nEndRow, bFiltered);
4500}
4501
4503{
4504 if (const ScTable* pTable = FetchTable(nTab))
4505 return pTable->FirstNonFilteredRow(nStartRow, nEndRow);
4506 return std::numeric_limits<SCROW>::max();
4507}
4508
4510{
4511 if (const ScTable* pTable = FetchTable(nTab))
4512 return pTable->LastNonFilteredRow(nStartRow, nEndRow);
4513 return std::numeric_limits<SCROW>::max();
4514}
4515
4517{
4518 if (const ScTable* pTable = FetchTable(nTab))
4519 return pTable->CountNonFilteredRows(nStartRow, nEndRow);
4520 return 0;
4521}
4522
4524{
4525 if (const ScTable* pTable = FetchTable(nTab))
4526 return pTable->IsManualRowHeight(nRow);
4527 return false;
4528}
4529
4531{
4532 for (const auto& pTable : maTabs)
4533 {
4534 if (pTable)
4535 pTable->SyncColRowFlags();
4536 }
4537}
4538
4540{
4541 if (const ScTable* pTable = FetchTable(nTab))
4542 return pTable->GetLastFlaggedRow();
4543 return 0;
4544}
4545
4547{
4548 if (const ScTable* pTable = FetchTable(nTab))
4549 return pTable->GetLastChangedColFlagsWidth();
4550 return 0;
4551}
4552
4554{
4555 if (const ScTable* pTable = FetchTable(nTab))
4556 return pTable->GetLastChangedRowFlagsWidth();
4557 return 0;
4558}
4559
4561{
4562 if (const ScTable* pTable = FetchTable(nTab))
4563 {
4564 CRFlags nStartFlags = pTable->GetColFlags(nStart);
4565 sal_uInt16 nStartWidth = pTable->GetOriginalWidth(nStart);
4566 for (SCCOL nCol : pTable->GetColumnsRange( nStart + 1, MaxCol()))
4567 {
4568 if (((nStartFlags & CRFlags::ManualBreak) != (pTable->GetColFlags(nCol) & CRFlags::ManualBreak)) ||
4569 (nStartWidth != pTable->GetOriginalWidth(nCol)) ||
4570 ((nStartFlags & CRFlags::Hidden) != (pTable->GetColFlags(nCol) & CRFlags::Hidden)) )
4571 {
4572 return nCol;
4573 }
4574 }
4575 return MaxCol()+1;
4576 }
4577 return 0;
4578}
4579
4581{
4582 const ScTable* pTable = FetchTable(nTab);
4583 if (!pTable)
4584 return 0;
4585
4586 const ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlagsArray = pTable->GetRowFlagsArray();
4587 if (!pRowFlagsArray)
4588 return 0;
4589
4590 if (!pTable->mpRowHeights || !pTable->mpHiddenRows)
4591 return 0;
4592
4593 size_t nIndex; // ignored
4594 SCROW nFlagsEndRow;
4595 SCROW nHiddenEndRow;
4596 SCROW nHeightEndRow;
4597 CRFlags nFlags;
4598 bool bHidden;
4599 sal_uInt16 nHeight;
4600 CRFlags nStartFlags = nFlags = pRowFlagsArray->GetValue( nStart, nIndex, nFlagsEndRow);
4601 bool bStartHidden = bHidden = pTable->RowHidden( nStart, nullptr, &nHiddenEndRow);
4602 sal_uInt16 nStartHeight = nHeight = pTable->GetRowHeight( nStart, nullptr, &nHeightEndRow, false);
4603 SCROW nRow;
4604 while ((nRow = std::min( nHiddenEndRow, std::min( nFlagsEndRow, nHeightEndRow)) + 1) <= MaxRow())
4605 {
4606 if (nFlagsEndRow < nRow)
4607 nFlags = pRowFlagsArray->GetValue( nRow, nIndex, nFlagsEndRow);
4608 if (nHiddenEndRow < nRow)
4609 bHidden = pTable->RowHidden( nRow, nullptr, &nHiddenEndRow);
4610 if (nHeightEndRow < nRow)
4611 nHeight = pTable->GetRowHeight( nRow, nullptr, &nHeightEndRow, false);
4612
4613 if (((nStartFlags & CRFlags::ManualBreak) != (nFlags & CRFlags::ManualBreak)) ||
4614 ((nStartFlags & CRFlags::ManualSize) != (nFlags & CRFlags::ManualSize)) ||
4615 (bStartHidden != bHidden) ||
4616 (nStartHeight != nHeight))
4617 return nRow;
4618 }
4619
4620 return MaxRow()+1;
4621}
4622
4623void ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault)
4624{
4625 nDefault = 0;
4626 ScDocAttrIterator aDocAttrItr(*this, nTab, nCol, 0, nCol, nLastRow);
4627 SCCOL nColumn;
4628 SCROW nStartRow;
4629 SCROW nEndRow;
4630 const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
4631 if (nEndRow >= nLastRow)
4632 return;
4633
4634 ScDefaultAttrSet aSet;
4635 ScDefaultAttrSet::iterator aItr = aSet.end();
4636 while (pAttr)
4637 {
4638 ScDefaultAttr aAttr(pAttr);
4639 aItr = aSet.find(aAttr);
4640 if (aItr == aSet.end())
4641 {
4642 aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
4643 aAttr.nFirst = nStartR