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