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