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 <detfunc.hxx>
70#include <scmod.hxx>
71#include <dociter.hxx>
72#include <progress.hxx>
73#include <autonamecache.hxx>
74#include <bcaslot.hxx>
75#include <postit.hxx>
76#include <clipparam.hxx>
77#include <defaultsoptions.hxx>
78#include <editutil.hxx>
79#include <stringutil.hxx>
80#include <formulaiter.hxx>
81#include <formulacell.hxx>
82#include <clipcontext.hxx>
83#include <listenercontext.hxx>
84#include <scopetools.hxx>
85#include <refupdatecontext.hxx>
86#include <formulagroup.hxx>
88#include <compressedarray.hxx>
89#include <recursionhelper.hxx>
90#include <SparklineGroup.hxx>
91#include <SparklineList.hxx>
92#include <undomanager.hxx>
93
95
96#include <limits>
97#include <memory>
98#include <utility>
99
100#include <comphelper/lok.hxx>
102
103#include <vcl/uitest/logger.hxx>
105
106#include <mtvelements.hxx>
107#include <sfx2/lokhelper.hxx>
108
109using ::editeng::SvxBorderLine;
110using namespace ::com::sun::star;
111
112namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
113using ::com::sun::star::uno::Sequence;
114using ::com::sun::star::sheet::TablePageBreakData;
115using ::std::set;
116
117namespace {
118
119std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
120{
121 SCTAB nTabStart = MAXTAB;
122 SCTAB nTabEnd = 0;
123 SCTAB nMax = static_cast<SCTAB>(rTables.size());
124 for (const auto& rTab : rMark)
125 {
126 if (rTab >= nMax)
127 break;
128
129 if (!rTables[rTab])
130 continue;
131
132 if (rTab < nTabStart)
133 nTabStart = rTab;
134 nTabEnd = rTab;
135 }
136
137 return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
138}
139
140void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
141{
142 EventDescription aDescription;
143 aDescription.aID = "grid_window";
144 aDescription.aAction = rAction;
145 aDescription.aParameters = std::move(aParameters);
146 aDescription.aParent = "MainWindow";
147 aDescription.aKeyWord = "ScGridWinUIObject";
148
149 UITestLogger::getInstance().logEvent(aDescription);
150}
151
152struct ScDefaultAttr
153{
154 const ScPatternAttr* pAttr;
155 SCROW nFirst;
157 explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {}
158};
159
160struct ScLessDefaultAttr
161{
162 bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const
163 {
164 return rValue1.pAttr < rValue2.pAttr;
165 }
166};
167
168}
169
170typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet;
171
172void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
173{
174 if ( !(ValidTab(nTab) && ( nTab >= static_cast<SCTAB>(maTabs.size()) ||!maTabs[nTab])) )
175 return;
176
177 // Get Custom prefix
178 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
179 OUString aString = rOpt.GetInitTabPrefix() + OUString::number(nTab+1);
180 if ( _bNeedsNameCheck )
181 CreateValidTabName( aString ); // no doubles
182 if (nTab < static_cast<SCTAB>(maTabs.size()))
183 {
184 maTabs[nTab].reset( new ScTable(*this, nTab, aString) );
185 }
186 else
187 {
188 while(nTab > static_cast<SCTAB>(maTabs.size()))
189 maTabs.push_back(nullptr);
190 maTabs.emplace_back( new ScTable(*this, nTab, aString) );
191 }
192 maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
193}
194
195bool ScDocument::HasTable( SCTAB nTab ) const
196{
197 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
198 if (maTabs[nTab])
199 return true;
200
201 return false;
202}
203
204bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
205{
206 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
207 {
208 if (maTabs[nTab])
209 {
210 rHashCode = maTabs[nTab]->GetHashCode();
211 return true;
212 }
213 }
214 return false;
215}
216
217bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
218{
219 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
220 {
221 if (maTabs[nTab])
222 {
223 rName = maTabs[nTab]->GetName();
224 return true;
225 }
226 }
227 rName.clear();
228 return false;
229}
230
231OUString ScDocument::GetCopyTabName( SCTAB nTab ) const
232{
233 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
234 return maTabNames[nTab];
235 return OUString();
236}
237
238bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
239{
240 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
241 {
242 if (maTabs[nTab])
243 {
244 maTabs[nTab]->SetCodeName( rName );
245 return true;
246 }
247 }
248 SAL_WARN("sc", "can't set code name " << rName );
249 return false;
250}
251
252bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
253{
254 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
255 if (maTabs[nTab])
256 {
257 rName = maTabs[nTab]->GetCodeName();
258 return true;
259 }
260 rName.clear();
261 return false;
262}
263
264bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
265{
266 static OUString aCacheName, aCacheUpperName;
267
269 if (aCacheName != rName)
270 {
271 aCacheName = rName;
272 // surprisingly slow ...
273 aCacheUpperName = ScGlobal::getCharClass().uppercase(rName);
274 }
275 const OUString aUpperName = aCacheUpperName;
276
277 for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++)
278 if (maTabs[i])
279 {
280 if (aUpperName == maTabs[i]->GetUpperName())
281 {
282 rTab = i;
283 return true;
284 }
285 }
286 rTab = 0;
287 return false;
288}
289
290std::vector<OUString> ScDocument::GetAllTableNames() const
291{
292 std::vector<OUString> aNames;
293 aNames.reserve(maTabs.size());
294 for (const auto& a : maTabs)
295 {
296 // Positions need to be preserved for ScCompiler and address convention
297 // context, so still push an empty string for NULL tabs.
298 OUString aName;
299 if (a)
300 {
301 const ScTable& rTab = *a;
302 aName = rTab.GetName();
303 }
304 aNames.push_back(aName);
305 }
306
307 return aNames;
308}
309
311{
312 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
313 return maTabs[nTab]->GetAnonymousDBData();
314 return nullptr;
315}
316
318{
319 return static_cast<SCTAB>(maTabs.size());
320}
321
322void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
323{
324 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
325 maTabs[nTab]->SetAnonymousDBData(std::move(pDBData));
326}
327
328void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
329{
330 mpAnonymousDBData = std::move(pDBData);
331}
332
334{
335 return mpAnonymousDBData.get();
336}
337
338bool ScDocument::ValidTabName( const OUString& rName )
339{
340 if (rName.isEmpty())
341 return false;
342 sal_Int32 nLen = rName.getLength();
343
344#if 1
345 // Restrict sheet names to what Excel accepts.
346 /* TODO: We may want to remove this restriction for full ODFF compliance.
347 * Merely loading and calculating ODF documents using these characters in
348 * sheet names is not affected by this, but all sheet name editing and
349 * copying functionality is, maybe falling back to "Sheet4" or similar. */
350 for (sal_Int32 i = 0; i < nLen; ++i)
351 {
352 const sal_Unicode c = rName[i];
353 switch (c)
354 {
355 case ':':
356 case '\\':
357 case '/':
358 case '?':
359 case '*':
360 case '[':
361 case ']':
362 // these characters are not allowed to match XL's convention.
363 return false;
364 case '\'':
365 if (i == 0 || i == nLen - 1)
366 // single quote is not allowed at the first or last
367 // character position.
368 return false;
369 break;
370 }
371 }
372#endif
373
374 return true;
375}
376
377bool ScDocument::ValidNewTabName( const OUString& rName ) const
378{
379 bool bValid = ValidTabName(rName);
380 if (!bValid)
381 return false;
382 OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
383 for (const auto& a : maTabs)
384 {
385 if (!a)
386 continue;
387 const OUString& rOldName = a->GetUpperName();
388 bValid = rOldName != aUpperName;
389 if (!bValid)
390 break;
391 }
392 return bValid;
393}
394
395void ScDocument::CreateValidTabName(OUString& rName) const
396{
397 if ( !ValidTabName(rName) )
398 {
399 // Find new one
400
401 // Get Custom prefix
402 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
403 const OUString& aStrTable = rOpt.GetInitTabPrefix();
404
405 bool bOk = false;
406
407 // First test if the prefix is valid, if so only avoid doubles
408 bool bPrefix = ValidTabName( aStrTable );
409 OSL_ENSURE(bPrefix, "Invalid Table Name");
410 SCTAB nDummy;
411
412 for ( SCTAB i = static_cast<SCTAB>(maTabs.size())+1; !bOk ; i++ )
413 {
414 rName = aStrTable + OUString::number(static_cast<sal_Int32>(i));
415 if (bPrefix)
416 bOk = ValidNewTabName( rName );
417 else
418 bOk = !GetTable( rName, nDummy );
419 }
420 }
421 else
422 {
423 // testing the supplied Name
424
425 if ( !ValidNewTabName(rName) )
426 {
427 SCTAB i = 1;
428 OUStringBuffer aName;
429 do
430 {
431 i++;
432 aName = rName;
433 aName.append('_');
434 aName.append(static_cast<sal_Int32>(i));
435 }
436 while (!ValidNewTabName(aName.toString()) && (i < MAXTAB+1));
437 rName = aName.makeStringAndClear();
438 }
439 }
440}
441
442void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
443{
444 aNames.clear();//ensure that the vector is empty
445
446 // Get Custom prefix
447 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
448 const OUString& aStrTable = rOpt.GetInitTabPrefix();
449
450 OUStringBuffer rName;
451
452 // First test if the prefix is valid, if so only avoid doubles
453 bool bPrefix = ValidTabName( aStrTable );
454 OSL_ENSURE(bPrefix, "Invalid Table Name");
455 SCTAB nDummy;
456 SCTAB i = static_cast<SCTAB>(maTabs.size())+1;
457
458 for (SCTAB j = 0; j < nCount; ++j)
459 {
460 bool bOk = false;
461 while(!bOk)
462 {
463 rName = aStrTable;
464 rName.append(static_cast<sal_Int32>(i));
465 if (bPrefix)
466 bOk = ValidNewTabName( rName.toString() );
467 else
468 bOk = !GetTable( rName.toString(), nDummy );
469 i++;
470 }
471 aNames.push_back(rName.makeStringAndClear());
472 }
473}
474
475void ScDocument::AppendTabOnLoad(const OUString& rName)
476{
477 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
478 if (!ValidTab(nTabCount))
479 // max table count reached. No more tables.
480 return;
481
482 OUString aName = rName;
484 maTabs.emplace_back( new ScTable(*this, nTabCount, aName) );
485}
486
487void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
488{
489 if (!ValidTab(nTab) || static_cast<SCTAB>(maTabs.size()) <= nTab)
490 return;
491
492 if (!ValidTabName(rName))
493 return;
494
495 maTabs[nTab]->SetName(rName);
496}
497
499{
500 for (const auto& a : maTabs)
501 {
502 if (a)
503 a->SetStreamValid(false);
504 }
505}
506
508 SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
509{
510 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
511 bool bValid = ValidTab(nTabCount);
512 if ( !bExternalDocument ) // else test rName == "'Doc'!Tab" first
513 bValid = (bValid && ValidNewTabName(rName));
514 if (bValid)
515 {
516 if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
517 {
518 nPos = maTabs.size();
519 maTabs.emplace_back( new ScTable(*this, nTabCount, rName) );
520 if ( bExternalDocument )
521 maTabs[nTabCount]->SetVisible( false );
522 }
523 else
524 {
525 if (ValidTab(nPos) && (nPos < nTabCount))
526 {
527 sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);
528
529 ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
530 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
531 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
532 if (pRangeName)
533 pRangeName->UpdateInsertTab(aCxt);
534 pDBCollection->UpdateReference(
535 URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
536 if (pDPCollection)
537 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
538 if (pDetOpList)
539 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
540 UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
541 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
542 if ( pUnoBroadcaster )
543 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
544
545 for (const auto& a : maTabs)
546 {
547 if (a)
548 a->UpdateInsertTab(aCxt);
549 }
550 maTabs.emplace(maTabs.begin() + nPos, new ScTable(*this, nPos, rName));
551
552 // UpdateBroadcastAreas must be called between UpdateInsertTab,
553 // which ends listening, and StartAllListeners, to not modify
554 // areas that are to be inserted by starting listeners.
555 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
556 for (const auto& a : maTabs)
557 {
558 if (a)
559 a->UpdateCompile();
560 }
561
563
564 if (pValidationList)
565 {
567 pValidationList->UpdateInsertTab(aCxt);
568 }
569
570 bValid = true;
571 }
572 else
573 bValid = false;
574 }
575 }
576
577 if (bValid)
578 {
580 aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
581 aCxt.mnTabDeletedStart = nPos;
582 aCxt.mnTabDeletedEnd = nPos;
584
586 {
587 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
589 }
590 }
591
592 return bValid;
593}
594
595bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
596 bool bNamesValid )
597{
598 SCTAB nNewSheets = static_cast<SCTAB>(rNames.size());
599 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
600 bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);
601
602 if (bValid)
603 {
604 if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
605 {
606 for ( SCTAB i = 0; i < nNewSheets; ++i )
607 {
608 maTabs.emplace_back( new ScTable(*this, nTabCount + i, rNames.at(i)) );
609 }
610 }
611 else
612 {
613 if (ValidTab(nPos) && (nPos < nTabCount))
614 {
615 sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
616 ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
617 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
618 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
619 if (pRangeName)
620 pRangeName->UpdateInsertTab(aCxt);
621 pDBCollection->UpdateReference(
622 URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
623 if (pDPCollection)
624 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
625 if (pDetOpList)
626 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets );
627 UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
628 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
629 if ( pUnoBroadcaster )
630 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );
631
632 for (const auto& a : maTabs)
633 {
634 if (a)
635 a->UpdateInsertTab(aCxt);
636 }
637 for (SCTAB i = 0; i < nNewSheets; ++i)
638 {
639 maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(*this, nPos + i, rNames.at(i)) );
640 }
641
642 // UpdateBroadcastAreas must be called between UpdateInsertTab,
643 // which ends listening, and StartAllListeners, to not modify
644 // areas that are to be inserted by starting listeners.
645 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
646 for (const auto& a : maTabs)
647 {
648 if (a)
649 a->UpdateCompile();
650 }
651
653
654 if (pValidationList)
655 {
657 pValidationList->UpdateInsertTab(aCxt);
658 }
659
660 bValid = true;
661 }
662 else
663 bValid = false;
664 }
665 }
666
667 if (bValid)
668 {
671 }
672
673 return bValid;
674}
675
677{
678 bool bValid = false;
679 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
680 {
681 if (maTabs[nTab])
682 {
683 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
684 if (nTabCount > 1)
685 {
686 sc::AutoCalcSwitch aACSwitch(*this, false);
687 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
688 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
689
690 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab );
691 DelBroadcastAreasInRange( aRange );
692
693 // #i8180# remove database ranges etc. that are on the deleted tab
694 // (restored in undo with ScRefUndoData)
695
696 xColNameRanges->DeleteOnTab( nTab );
697 xRowNameRanges->DeleteOnTab( nTab );
698 pDBCollection->DeleteOnTab( nTab );
699 if (pDPCollection)
700 pDPCollection->DeleteOnTab( nTab );
701 if (pDetOpList)
702 pDetOpList->DeleteOnTab( nTab );
703 DeleteAreaLinksOnTab( nTab );
704
705 // normal reference update
706
707 aRange.aEnd.SetTab( static_cast<SCTAB>(maTabs.size())-1 );
708 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
709 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
710 if (pRangeName)
711 pRangeName->UpdateDeleteTab(aCxt);
712 pDBCollection->UpdateReference(
713 URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
714 if (pDPCollection)
715 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
716 if (pDetOpList)
717 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 );
718 UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
719 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
720 if (pValidationList)
721 {
723 pValidationList->UpdateDeleteTab(aCxt);
724 }
725 if ( pUnoBroadcaster )
726 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );
727
728 for (auto & pTab : maTabs)
729 if (pTab)
730 pTab->UpdateDeleteTab(aCxt);
731
732 // tdf#149502 make sure ScTable destructor called after the erase is finished, when
733 // maTabs[x].nTab==x is true again, as it should be always true.
734 // In the end of maTabs.erase, maTabs indexes change, but nTab updated before erase.
735 // ~ScTable expect that maTabs[x].nTab==x so it shouldn't be called during erase.
736 ScTableUniquePtr pErasedTab = std::move(maTabs[nTab]);
737 maTabs.erase(maTabs.begin() + nTab);
738 delete pErasedTab.release();
739
740 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
741 // which ends listening, and StartAllListeners, to not modify
742 // areas that are to be inserted by starting listeners.
743 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
744 for (const auto& a : maTabs)
745 {
746 if (a)
747 a->UpdateCompile();
748 }
749 // Excel-Filter deletes some Tables while loading, Listeners will
750 // only be triggered after the loading is done.
752 {
754
755 sc::SetFormulaDirtyContext aFormulaDirtyCxt;
756 SetAllFormulasDirty(aFormulaDirtyCxt);
757 }
758
760 {
761 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
763 }
764
765 bValid = true;
766 }
767 }
768 }
769 return bValid;
770}
771
772bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
773{
774 bool bValid = false;
775 if (ValidTab(nTab) && (nTab + nSheets) <= static_cast<SCTAB>(maTabs.size()))
776 {
777 if (maTabs[nTab])
778 {
779 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
780 if (nTabCount > nSheets)
781 {
782 sc::AutoCalcSwitch aACSwitch(*this, false);
783 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
784 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
785
786 for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
787 {
788 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab + aTab );
789 DelBroadcastAreasInRange( aRange );
790
791 // #i8180# remove database ranges etc. that are on the deleted tab
792 // (restored in undo with ScRefUndoData)
793
794 xColNameRanges->DeleteOnTab( nTab + aTab );
795 xRowNameRanges->DeleteOnTab( nTab + aTab );
796 pDBCollection->DeleteOnTab( nTab + aTab );
797 if (pDPCollection)
798 pDPCollection->DeleteOnTab( nTab + aTab );
799 if (pDetOpList)
800 pDetOpList->DeleteOnTab( nTab + aTab );
801 DeleteAreaLinksOnTab( nTab + aTab );
802 }
803
804 if (pRangeName)
805 pRangeName->UpdateDeleteTab(aCxt);
806
807 // normal reference update
808
809 ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTabCount - 1 );
810 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
811 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
812 pDBCollection->UpdateReference(
813 URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
814 if (pDPCollection)
815 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
816 if (pDetOpList)
817 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets );
818 UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
819 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
820 if (pValidationList)
821 {
823 pValidationList->UpdateDeleteTab(aCxt);
824 }
825 if ( pUnoBroadcaster )
826 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );
827
828 for (auto & pTab : maTabs)
829 if (pTab)
830 pTab->UpdateDeleteTab(aCxt);
831
832 maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
833 // UpdateBroadcastAreas must be called between UpdateDeleteTab,
834 // which ends listening, and StartAllListeners, to not modify
835 // areas that are to be inserted by starting listeners.
836 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
837 for (const auto& a : maTabs)
838 {
839 if (a)
840 a->UpdateCompile();
841 }
842 // Excel-Filter deletes some Tables while loading, Listeners will
843 // only be triggered after the loading is done.
845 {
847
848 sc::SetFormulaDirtyContext aFormulaDirtyCxt;
849 SetAllFormulasDirty(aFormulaDirtyCxt);
850 }
851
853 {
854 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
856 }
857
858 bValid = true;
859 }
860 }
861 }
862 return bValid;
863}
864
865bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
866{
867 bool bValid = false;
868 SCTAB i;
869 if (ValidTab(nTab))
870 {
871 if (maTabs[nTab])
872 {
873 if ( bExternalDocument )
874 bValid = true; // composed name
875 else
876 bValid = ValidTabName(rName);
877 for (i=0; (i< static_cast<SCTAB>(maTabs.size())) && bValid; i++)
878 if (maTabs[i] && (i != nTab))
879 {
880 OUString aOldName = maTabs[i]->GetName();
881 bValid = !ScGlobal::GetTransliteration().isEqual( rName, aOldName );
882 }
883 if (bValid)
884 {
885 // #i75258# update charts before renaming, so they can get their live data objects.
886 // Once the charts are live, the sheet can be renamed without problems.
888 pChartListenerCollection->UpdateChartsContainingTab( nTab );
889 maTabs[nTab]->SetName(rName);
890
891 // If formulas refer to the renamed sheet, the TokenArray remains valid,
892 // but the XML stream must be re-generated.
893 for (const auto& a : maTabs)
894 {
895 if (a)
896 a->SetStreamValid( false );
897 }
898
900 {
901 ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
903 }
904 }
905 }
906 }
907
908 collectUIInformation({{"NewName", rName}}, "Rename_Sheet");
909
910 return bValid;
911}
912
913void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
914{
915 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
916 if (maTabs[nTab])
917 maTabs[nTab]->SetVisible(bVisible);
918}
919
920bool ScDocument::IsVisible( SCTAB nTab ) const
921{
922 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
923 if (maTabs[nTab])
924 return maTabs[nTab]->IsVisible();
925
926 return false;
927}
928
930{
931 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
932 return maTabs[nTab]->IsStreamValid();
933
934 return false;
935}
936
937void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
938{
939 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
940 maTabs[nTab]->SetStreamValid( bSet, bIgnoreLock );
941}
942
944{
945 mbStreamValidLocked = bLock;
946}
947
949{
950 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
951 return maTabs[nTab]->IsPendingRowHeights();
952
953 return false;
954}
955
957{
958 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
959 maTabs[nTab]->SetPendingRowHeights( bSet );
960}
961
962void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling)
963{
964 if ( !(ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) )
965 return;
966
967 if ( bImportingXML )
968 {
969 // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
970 // is applied in SetImportingXML(false). This is so the shapes can be loaded in
971 // normal LTR mode.
972
973 maTabs[nTab]->SetLoadingRTL( bRTL );
974 return;
975 }
976
977 maTabs[nTab]->SetLayoutRTL( bRTL ); // only sets the flag
978 maTabs[nTab]->SetDrawPageSize(true, true, eObjectHandling);
979
980 // objects are already repositioned via SetDrawPageSize, only writing mode is missing
981 if (!mpDrawLayer)
982 return;
983
984 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
985 OSL_ENSURE(pPage,"Page ?");
986 if (!pPage)
987 return;
988
989 SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
990 SdrObject* pObject = aIter.Next();
991 while (pObject)
992 {
993 pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
994 pObject = aIter.Next();
995 }
996}
997
999{
1000 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
1001 return maTabs[nTab]->IsLayoutRTL();
1002
1003 return false;
1004}
1005
1007{
1008 // Negative page area is always used for RTL layout.
1009 // The separate method is used to find all RTL handling of drawing objects.
1010 return IsLayoutRTL( nTab );
1011}
1012
1013/* ----------------------------------------------------------------------------
1014 used search area:
1015
1016 GetCellArea - Only Data
1017 GetTableArea - Data / Attributes
1018 GetPrintArea - intended for character objects,
1019 sweeps attributes all the way to bottom / right
1020---------------------------------------------------------------------------- */
1021
1022bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
1023{
1024 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1025 if (maTabs[nTab])
1026 return maTabs[nTab]->GetCellArea( rEndCol, rEndRow );
1027
1028 rEndCol = 0;
1029 rEndRow = 0;
1030 return false;
1031}
1032
1033bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
1034{
1035 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1036 if (maTabs[nTab])
1037 return maTabs[nTab]->GetTableArea( rEndCol, rEndRow, bCalcHiddens);
1038
1039 rEndCol = 0;
1040 rEndRow = 0;
1041 return false;
1042}
1043
1044bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
1045{
1046 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
1047 return false;
1048
1049 SCCOL nCol1, nCol2;
1050 SCROW nRow1, nRow2;
1051 maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
1052 maTabs[nTab]->GetLastDataPos(nCol2, nRow2);
1053
1054 if (nCol1 > nCol2 || nRow1 > nRow2)
1055 // invalid range.
1056 return false;
1057
1058 // Make sure the area only shrinks, and doesn't grow.
1059 if (rStartCol < nCol1)
1060 rStartCol = nCol1;
1061 if (nCol2 < rEndCol)
1062 rEndCol = nCol2;
1063 if (rStartRow < nRow1)
1064 rStartRow = nRow1;
1065 if (nRow2 < rEndRow)
1066 rEndRow = nRow2;
1067
1068 if (rStartCol > rEndCol || rStartRow > rEndRow)
1069 // invalid range.
1070 return false;
1071
1072 return true; // success!
1073}
1074
1075bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
1076 SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
1077 bool bStickyTopRow, bool bStickyLeftCol, ScDataAreaExtras* pDataAreaExtras ) const
1078{
1079 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
1080 {
1081 o_bShrunk = false;
1082 return false;
1083 }
1084 return maTabs[nTab]->ShrinkToUsedDataArea(
1085 o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, bColumnsOnly, bStickyTopRow,
1086 bStickyLeftCol, pDataAreaExtras);
1087}
1088
1089SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
1090{
1091 const ScTable* pTab = FetchTable(nTab);
1092 if (!pTab)
1093 return -1;
1094
1095 return pTab->GetLastDataRow(nCol1, nCol2, nLastRow);
1096}
1097
1098// connected area
1099
1100void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1101 SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
1102{
1103 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1104 maTabs[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
1105}
1106
1108{
1109 SCTAB nTab = rRange.aStart.Tab();
1110 if (nTab != rRange.aEnd.Tab())
1111 return true;
1112
1113 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1114 return maTabs[nTab]->GetDataAreaSubrange(rRange);
1115
1116 return true;
1117}
1118
1119void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
1120 SCCOL& rEndCol, SCROW& rEndRow )
1121{
1122 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
1123 if (maTabs[nTab])
1124 maTabs[nTab]->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow );
1125}
1126
1128{
1129 ScRangeListRef aNew = new ScRangeList;
1130 if (rRangeList.is())
1131 {
1132 for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
1133 {
1134 ScRange aRange( (*rRangeList)[i] );
1135 if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MaxCol() ) ||
1136 ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MaxRow() ) )
1137 {
1138 SCCOL nStartCol = aRange.aStart.Col();
1139 SCROW nStartRow = aRange.aStart.Row();
1140 SCCOL nEndCol = aRange.aEnd.Col();
1141 SCROW nEndRow = aRange.aEnd.Row();
1142 SCTAB nTab = aRange.aStart.Tab();
1143 if ( nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
1144 maTabs[nTab]->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
1145 aRange.aStart.SetCol( nStartCol );
1146 aRange.aStart.SetRow( nStartRow );
1147 aRange.aEnd.SetCol( nEndCol );
1148 aRange.aEnd.SetRow( nEndRow );
1149 }
1150 aNew->push_back(aRange);
1151 }
1152 }
1153 else
1154 {
1155 OSL_FAIL("LimitChartIfAll: Ref==0");
1156 }
1157 rRangeList = aNew;
1158}
1159
1160static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1161{
1162 // without ScMarkData, leave start/end unchanged
1163 if ( !pTabMark )
1164 return;
1165
1166 for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
1167 if (pTabMark->GetTableSelect(nTab))
1168 {
1169 // find first range of consecutive selected sheets
1170 rTabRangeStart = pTabMark->GetFirstSelected();
1171 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1172 ++nTab;
1173 rTabRangeEnd = nTab;
1174 return;
1175 }
1176}
1177
1178static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
1179{
1180 if ( pTabMark )
1181 {
1182 // find next range of consecutive selected sheets after rTabRangeEnd
1183 for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
1184 if (pTabMark->GetTableSelect(nTab))
1185 {
1186 rTabRangeStart = nTab;
1187 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
1188 ++nTab;
1189 rTabRangeEnd = nTab;
1190 return true;
1191 }
1192 }
1193 return false;
1194}
1195
1196bool ScDocument::CanInsertRow( const ScRange& rRange ) const
1197{
1198 SCCOL nStartCol = rRange.aStart.Col();
1199 SCROW nStartRow = rRange.aStart.Row();
1200 SCTAB nStartTab = rRange.aStart.Tab();
1201 SCCOL nEndCol = rRange.aEnd.Col();
1202 SCROW nEndRow = rRange.aEnd.Row();
1203 SCTAB nEndTab = rRange.aEnd.Tab();
1204 PutInOrder( nStartCol, nEndCol );
1205 PutInOrder( nStartRow, nEndRow );
1206 PutInOrder( nStartTab, nEndTab );
1207 SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
1208
1209 bool bTest = true;
1210 for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1211 if (maTabs[i])
1212 bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );
1213
1214 return bTest;
1215}
1216
1217namespace {
1218
1219struct SetDirtyIfPostponedHandler
1220{
1221 void operator() (const ScTableUniquePtr & p)
1222 {
1223 if (p)
1224 p->SetDirtyIfPostponed();
1225 }
1226};
1227
1228struct BroadcastRecalcOnRefMoveHandler
1229{
1230 void operator() (const ScTableUniquePtr & p)
1231 {
1232 if (p)
1233 p->BroadcastRecalcOnRefMove();
1234 }
1235};
1236
1237struct BroadcastRecalcOnRefMoveGuard
1238{
1239 explicit BroadcastRecalcOnRefMoveGuard( ScDocument* pDoc ) :
1240 aSwitch( *pDoc, false),
1241 aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
1242 {
1243 }
1244
1245private:
1246 sc::AutoCalcSwitch aSwitch; // first for ctor/dtor order, destroy second
1247 ScBulkBroadcast aBulk; // second for ctor/dtor order, destroy first
1248};
1249
1250}
1251
1252bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
1253 SCCOL nEndCol, SCTAB nEndTab,
1254 SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
1255 const ScMarkData* pTabMark )
1256{
1257 SCTAB i;
1258
1259 PutInOrder( nStartCol, nEndCol );
1260 PutInOrder( nStartTab, nEndTab );
1261 if ( pTabMark )
1262 {
1263 nStartTab = 0;
1264 nEndTab = static_cast<SCTAB>(maTabs.size()) -1;
1265 }
1266
1267 bool bTest = true;
1268 bool bRet = false;
1269 bool bOldAutoCalc = GetAutoCalc();
1270 SetAutoCalc( false ); // avoid multiple calculations
1271 bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
1273 for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1274 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1275 bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
1276 if (bTest)
1277 {
1278 // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
1279 // aren't shifted that would be rebuild at UpdateReference
1280
1281 // handle chunks of consecutive selected sheets together
1282 SCTAB nTabRangeStart = nStartTab;
1283 SCTAB nTabRangeEnd = nEndTab;
1284 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1285 ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
1286 sc::EndListeningContext aEndListenCxt(*this);
1287
1288 std::vector<ScAddress> aGroupPos;
1289 do
1290 {
1291 aShiftedRange.aStart.SetTab(nTabRangeStart);
1292 aShiftedRange.aEnd.SetTab(nTabRangeEnd);
1293
1294 // Collect all formula groups that will get split by the shifting,
1295 // and end all their listening. Record the position of the top
1296 // cell of the topmost group, and the position of the bottom cell
1297 // of the bottommost group.
1298 EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);
1299
1300 UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
1301 }
1302 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1303
1304 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1305
1306 sc::RefUpdateContext aCxt(*this);
1307 aCxt.meMode = URM_INSDEL;
1308 aCxt.maRange = aShiftedRange;
1309 aCxt.mnRowDelta = nSize;
1310 do
1311 {
1312 aCxt.maRange.aStart.SetTab(nTabRangeStart);
1313 aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
1314 UpdateReference(aCxt, pRefUndoDoc, false); // without drawing objects
1315 }
1316 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1317
1318 // UpdateReference should have set "needs listening" flags to those
1319 // whose references have been modified. We also need to set this flag
1320 // to those that were in the groups that got split by shifting.
1321 SetNeedsListeningGroups(aGroupPos);
1322
1323 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1324 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1325 maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
1326
1327 // UpdateRef for drawing layer must be after inserting,
1328 // when the new row heights are known.
1329 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1330 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1331 maTabs[i]->UpdateDrawRef( URM_INSDEL,
1332 nStartCol, nStartRow, nStartTab, nEndCol, MaxRow(), nEndTab,
1333 0, static_cast<SCROW>(nSize), 0 );
1334
1335 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1336 { // A new Listening is needed when references to deleted ranges are restored,
1337 // previous Listeners were removed in FormulaCell UpdateReference.
1339 }
1340 else
1341 { // Listeners have been removed in UpdateReference
1343
1344 // At least all cells using range names pointing relative to the
1345 // moved range must be recalculated, and all cells marked postponed
1346 // dirty.
1347 for (const auto& a : maTabs)
1348 {
1349 if (a)
1350 a->SetDirtyIfPostponed();
1351 }
1352
1353 {
1354 BroadcastRecalcOnRefMoveGuard g(this);
1355 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1356 }
1357 }
1358 bRet = true;
1359 }
1360 EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
1361 SetAutoCalc( bOldAutoCalc );
1362 if ( bRet && pChartListenerCollection )
1363 pChartListenerCollection->UpdateDirtyCharts();
1364 return bRet;
1365}
1366
1367bool ScDocument::InsertRow( const ScRange& rRange )
1368{
1369 return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1370 rRange.aEnd.Col(), rRange.aEnd.Tab(),
1371 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1372}
1373
1374void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
1375 SCCOL nEndCol, SCTAB nEndTab,
1376 SCROW nStartRow, SCSIZE nSize,
1377 ScDocument* pRefUndoDoc, bool* pUndoOutline,
1378 const ScMarkData* pTabMark )
1379{
1380 SCTAB i;
1381
1382 PutInOrder( nStartCol, nEndCol );
1383 PutInOrder( nStartTab, nEndTab );
1384 if ( pTabMark )
1385 {
1386 nStartTab = 0;
1387 nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1388 }
1389
1390 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1391
1392 // handle chunks of consecutive selected sheets together
1393 SCTAB nTabRangeStart = nStartTab;
1394 SCTAB nTabRangeEnd = nEndTab;
1395 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1396 do
1397 {
1398 if ( ValidRow(nStartRow+nSize) )
1399 {
1401 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1402 ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
1404 ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
1405 ScAddress( nEndCol, MaxRow(), nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
1406 }
1407 else
1409 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1410 ScAddress( nEndCol, MaxRow(), nTabRangeEnd ) ) );
1411 }
1412 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1413
1414 sc::RefUpdateContext aCxt(*this);
1415 const bool bLastRowIncluded = (static_cast<SCROW>(nStartRow + nSize) == GetMaxRowCount() && ValidRow(nStartRow));
1416 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1417 {
1418 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1419 aCxt.meMode = URM_INSDEL;
1420 aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
1421 if (bLastRowIncluded)
1422 {
1423 // Last row is included, shift a virtually non-existent row in.
1424 aCxt.maRange = ScRange( nStartCol, GetMaxRowCount(), nTabRangeStart, nEndCol, GetMaxRowCount(), nTabRangeEnd);
1425 }
1426 else
1427 {
1428 aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
1429 }
1430 do
1431 {
1432 UpdateReference(aCxt, pRefUndoDoc, true, false);
1433 }
1434 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1435 }
1436
1437 if (pUndoOutline)
1438 *pUndoOutline = false;
1439
1440 // Keep track of the positions of all formula groups that have been joined
1441 // during row deletion.
1442 std::vector<ScAddress> aGroupPos;
1443
1444 for ( i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1445 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1446 maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
1447
1448 // Newly joined groups have some of their members still listening. We
1449 // need to make sure none of them are listening.
1450 EndListeningGroups(aGroupPos);
1451
1452 // Mark all joined groups for group listening.
1453 SetNeedsListeningGroups(aGroupPos);
1454
1455 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
1456 {
1457 // Listeners have been removed in UpdateReference
1459
1460 // At least all cells using range names pointing relative to the moved
1461 // range must be recalculated, and all cells marked postponed dirty.
1462 for (const auto& a : maTabs)
1463 {
1464 if (a)
1465 a->SetDirtyIfPostponed();
1466 }
1467
1468 {
1469 BroadcastRecalcOnRefMoveGuard g(this);
1470 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1471 }
1472 }
1473
1475 pChartListenerCollection->UpdateDirtyCharts();
1476}
1477
1478void ScDocument::DeleteRow( const ScRange& rRange )
1479{
1480 DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
1481 rRange.aEnd.Col(), rRange.aEnd.Tab(),
1482 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
1483}
1484
1485bool ScDocument::CanInsertCol( const ScRange& rRange ) const
1486{
1487 SCCOL nStartCol = rRange.aStart.Col();
1488 SCROW nStartRow = rRange.aStart.Row();
1489 SCTAB nStartTab = rRange.aStart.Tab();
1490 SCCOL nEndCol = rRange.aEnd.Col();
1491 SCROW nEndRow = rRange.aEnd.Row();
1492 SCTAB nEndTab = rRange.aEnd.Tab();
1493 PutInOrder( nStartCol, nEndCol );
1494 PutInOrder( nStartRow, nEndRow );
1495 PutInOrder( nStartTab, nEndTab );
1496 SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);
1497
1498 bool bTest = true;
1499 for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1500 if (maTabs[i])
1501 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1502
1503 return bTest;
1504}
1505
1506bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
1507 SCROW nEndRow, SCTAB nEndTab,
1508 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1509 const ScMarkData* pTabMark )
1510{
1511 SCTAB i;
1512
1513 PutInOrder( nStartRow, nEndRow );
1514 PutInOrder( nStartTab, nEndTab );
1515 if ( pTabMark )
1516 {
1517 nStartTab = 0;
1518 nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1519 }
1520
1521 bool bTest = true;
1522 bool bRet = false;
1523 bool bOldAutoCalc = GetAutoCalc();
1524 SetAutoCalc( false ); // avoid multiple calculations
1525 bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
1527 for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
1528 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1529 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
1530 if (bTest)
1531 {
1532 // handle chunks of consecutive selected sheets together
1533 SCTAB nTabRangeStart = nStartTab;
1534 SCTAB nTabRangeEnd = nEndTab;
1535 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1536 do
1537 {
1539 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1540 ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
1541 }
1542 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1543
1544 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1545
1546 sc::RefUpdateContext aCxt(*this);
1547 aCxt.meMode = URM_INSDEL;
1548 aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MaxCol(), nEndRow, nTabRangeEnd);
1549 aCxt.mnColDelta = nSize;
1550 do
1551 {
1552 UpdateReference(aCxt, pRefUndoDoc, true, false);
1553 }
1554 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1555
1556 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
1557 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1558 maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);
1559
1560 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
1561 { // A new Listening is needed when references to deleted ranges are restored,
1562 // previous Listeners were removed in FormulaCell UpdateReference.
1564 }
1565 else
1566 {
1567 // Listeners have been removed in UpdateReference
1569 // At least all cells using range names pointing relative to the
1570 // moved range must be recalculated, and all cells marked postponed
1571 // dirty.
1572 {
1573 ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
1574 std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
1575 }
1576 // Cells containing functions such as CELL, COLUMN or ROW may have
1577 // changed their values on relocation. Broadcast them.
1578 {
1579 BroadcastRecalcOnRefMoveGuard g(this);
1580 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1581 }
1582 }
1583 bRet = true;
1584 }
1585 EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
1586 SetAutoCalc( bOldAutoCalc );
1587 if ( bRet && pChartListenerCollection )
1588 pChartListenerCollection->UpdateDirtyCharts();
1589 return bRet;
1590}
1591
1592bool ScDocument::InsertCol( const ScRange& rRange )
1593{
1594 return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
1595 rRange.aEnd.Row(), rRange.aEnd.Tab(),
1596 rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
1597}
1598
1599void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
1600 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
1601 bool* pUndoOutline, const ScMarkData* pTabMark )
1602{
1603 SCTAB i;
1604
1605 PutInOrder( nStartRow, nEndRow );
1606 PutInOrder( nStartTab, nEndTab );
1607 if ( pTabMark )
1608 {
1609 nStartTab = 0;
1610 nEndTab = static_cast<SCTAB>(maTabs.size())-1;
1611 }
1612
1613 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
1614 ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
1615
1616 // handle chunks of consecutive selected sheets together
1617 SCTAB nTabRangeStart = nStartTab;
1618 SCTAB nTabRangeEnd = nEndTab;
1619 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1620 do
1621 {
1622 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
1623 {
1625 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1626 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
1628 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
1629 ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
1630 }
1631 else
1633 ScAddress( nStartCol, nStartRow, nTabRangeStart ),
1634 ScAddress( MaxCol(), nEndRow, nTabRangeEnd ) ) );
1635 }
1636 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1637
1638 sc::RefUpdateContext aCxt(*this);
1639 const bool bLastColIncluded = (static_cast<SCCOL>(nStartCol + nSize) == GetMaxColCount() && ValidCol(nStartCol));
1640 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1641 {
1642 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
1643 aCxt.meMode = URM_INSDEL;
1644 aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
1645 if (bLastColIncluded)
1646 {
1647 // Last column is included, shift a virtually non-existent column in.
1648 aCxt.maRange = ScRange( GetMaxColCount(), nStartRow, nTabRangeStart, GetMaxColCount(), nEndRow, nTabRangeEnd);
1649 }
1650 else
1651 {
1652 aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
1653 MaxCol(), nEndRow, nTabRangeEnd);
1654 }
1655 do
1656 {
1657 UpdateReference(aCxt, pRefUndoDoc, true, false);
1658 }
1659 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
1660 }
1661
1662 if (pUndoOutline)
1663 *pUndoOutline = false;
1664
1665 for (i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); ++i)
1666 {
1667 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
1668 maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
1669 }
1670
1671 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
1672 {
1673 // Listeners have been removed in UpdateReference
1675
1676 // At least all cells using range names pointing relative to the moved
1677 // range must be recalculated, and all cells marked postponed dirty.
1678 for (const auto& a : maTabs)
1679 {
1680 if (a)
1681 a->SetDirtyIfPostponed();
1682 }
1683
1684 {
1685 BroadcastRecalcOnRefMoveGuard g(this);
1686 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
1687 }
1688 }
1689
1691 pChartListenerCollection->UpdateDirtyCharts();
1692}
1693
1694void ScDocument::DeleteCol( const ScRange& rRange )
1695{
1696 DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(),
1697 rRange.aEnd.Row(), rRange.aEnd.Tab(),
1698 rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
1699}
1700
1701// for Area-Links: Insert/delete cells, when the range is changed.
1702// (without Paint)
1703
1704static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew,
1705 ScRange& rColRange, bool& rInsCol, bool& rDelCol,
1706 ScRange& rRowRange, bool& rInsRow, bool& rDelRow )
1707{
1708 OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" );
1709
1710 rInsCol = rDelCol = rInsRow = rDelRow = false;
1711
1712 SCCOL nStartX = rOld.aStart.Col();
1713 SCROW nStartY = rOld.aStart.Row();
1714 SCCOL nOldEndX = rOld.aEnd.Col();
1715 SCROW nOldEndY = rOld.aEnd.Row();
1716 SCCOL nNewEndX = rNew.aEnd.Col();
1717 SCROW nNewEndY = rNew.aEnd.Row();
1718 SCTAB nTab = rOld.aStart.Tab();
1719
1720 // if more rows, columns are inserted/deleted at the old height.
1721 bool bGrowY = ( nNewEndY > nOldEndY );
1722 SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY;
1723 SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX;
1724
1725 // Columns
1726
1727 if ( nNewEndX > nOldEndX ) // Insert columns
1728 {
1729 rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab );
1730 rInsCol = true;
1731 }
1732 else if ( nNewEndX < nOldEndX ) // Delete columns
1733 {
1734 rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab );
1735 rDelCol = true;
1736 }
1737
1738 // Rows
1739
1740 if ( nNewEndY > nOldEndY ) // Insert rows
1741 {
1742 rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab );
1743 rInsRow = true;
1744 }
1745 else if ( nNewEndY < nOldEndY ) // Delete rows
1746 {
1747 rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab );
1748 rDelRow = true;
1749 }
1750}
1751
1753{
1754 bool bPart = false;
1755 SCTAB nTab = rRange.aStart.Tab();
1756
1757 SCCOL nStartX = rRange.aStart.Col();
1758 SCROW nStartY = rRange.aStart.Row();
1759 SCCOL nEndX = rRange.aEnd.Col();
1760 SCROW nEndY = rRange.aEnd.Row();
1761
1762 if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab,
1764 {
1765 ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
1766 ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab );
1767
1768 bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() ||
1769 nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() );
1770 }
1771 return bPart;
1772}
1773
1775{
1776 SCTAB nTab = rPos.Tab();
1777 if (!TableExists(nTab))
1778 return formula::FormulaTokenRef();
1779
1780 return maTabs[nTab]->ResolveStaticReference(rPos.Col(), rPos.Row());
1781}
1782
1784{
1785 SCTAB nTab = rRange.aStart.Tab();
1786 if (nTab != rRange.aEnd.Tab() || !TableExists(nTab))
1787 return formula::FormulaTokenRef();
1788
1789 return maTabs[nTab]->ResolveStaticReference(
1790 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
1791}
1792
1794{
1795 SCTAB nTab = rPos.Tab();
1796 if (!TableExists(nTab))
1797 return formula::VectorRefArray();
1798
1799 return maTabs[nTab]->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1800}
1801
1802#ifdef DBG_UTIL
1804{
1805 SCTAB nTab = rPos.Tab();
1806 assert(TableExists(nTab));
1807 return maTabs[nTab]->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
1808}
1809#endif
1810
1812{
1813 assert(nAdjustHeightLock > 0);
1814 if(nAdjustHeightLock > 0)
1816}
1817
1819{
1820 SCTAB nTab = rPos.Tab();
1821 if (!TableExists(nTab))
1822 return false;
1823
1824 return maTabs[nTab]->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup);
1825}
1826
1827bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
1828{
1829 if ( rOld == rNew )
1830 return true;
1831
1832 bool bOk = true;
1833 bool bInsCol,bDelCol,bInsRow,bDelRow;
1834 ScRange aColRange,aRowRange;
1835 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1836
1837 if ( bInsCol && !CanInsertCol( aColRange ) ) // Cells at the edge ?
1838 bOk = false;
1839 if ( bInsRow && !CanInsertRow( aRowRange ) ) // Cells at the edge ?
1840 bOk = false;
1841
1842 if ( bInsCol || bDelCol )
1843 {
1844 aColRange.aEnd.SetCol(MaxCol());
1845 if ( HasPartOfMerged(aColRange) )
1846 bOk = false;
1847 }
1848 if ( bInsRow || bDelRow )
1849 {
1850 aRowRange.aEnd.SetRow(MaxRow());
1851 if ( HasPartOfMerged(aRowRange) )
1852 bOk = false;
1853 }
1854
1855 return bOk;
1856}
1857
1858void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
1859{
1860 if (bClear)
1862
1863 bool bInsCol,bDelCol,bInsRow,bDelRow;
1864 ScRange aColRange,aRowRange;
1865 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
1866
1867 if ( bInsCol )
1868 InsertCol( aColRange ); // First insert columns
1869 if ( bInsRow )
1870 InsertRow( aRowRange );
1871
1872 if ( bDelRow )
1873 DeleteRow( aRowRange ); // First delete rows
1874 if ( bDelCol )
1875 DeleteCol( aColRange );
1876
1877 // Expand references to inserted rows
1878
1879 if ( bInsCol || bInsRow )
1880 {
1881 ScRange aGrowSource = rOld;
1882 aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
1883 aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
1884 SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
1885 SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
1886 UpdateGrow( aGrowSource, nGrowX, nGrowY );
1887 }
1888}
1889
1891 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
1892 InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
1893{
1894 sc::AutoCalcSwitch aACSwitch(*this, false);
1895
1896 PutInOrder( nCol1, nCol2 );
1897 PutInOrder( nRow1, nRow2 );
1898
1899 std::vector<ScAddress> aGroupPos;
1900 // Destroy and reconstruct listeners only if content is affected.
1901 bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
1902 if (bDelContent)
1903 {
1904 // Record the positions of top and/or bottom formula groups that intersect
1905 // the area borders.
1906 sc::EndListeningContext aCxt(*this);
1907 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1908 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1909 {
1910 if (rMark.GetTableSelect(i))
1911 {
1912 aRange.aStart.SetTab(i);
1913 aRange.aEnd.SetTab(i);
1914
1915 EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
1916 }
1917 }
1919 }
1920
1921 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1922 if (maTabs[i])
1923 if ( rMark.GetTableSelect(i) || bIsUndo )
1924 maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
1925
1926 if (!bDelContent)
1927 return;
1928
1929 // Re-start listeners on those top bottom groups that have been split.
1930 SetNeedsListeningGroups(aGroupPos);
1932
1933 // If formula groups were split their listeners were destroyed and may
1934 // need to be notified now that they're restored, ScTable::DeleteArea()
1935 // couldn't do that.
1936 if (aGroupPos.empty())
1937 return;
1938
1939 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
1940 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
1941 {
1942 if (rMark.GetTableSelect(i))
1943 {
1944 aRange.aStart.SetTab(i);
1945 aRange.aEnd.SetTab(i);
1946 SetDirty( aRange, true);
1947 }
1948 }
1949}
1950
1952 SCCOL nCol2, SCROW nRow2,
1953 SCTAB nTab, InsertDeleteFlags nDelFlag)
1954{
1955 PutInOrder( nCol1, nCol2 );
1956 PutInOrder( nRow1, nRow2 );
1957 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
1958 {
1959 bool bOldAutoCalc = GetAutoCalc();
1960 SetAutoCalc( false ); // avoid multiple calculations
1961 maTabs[nTab]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag);
1962 SetAutoCalc( bOldAutoCalc );
1963 }
1964}
1965
1967{
1968 for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ )
1969 DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(),
1970 rRange.aEnd.Col(), rRange.aEnd.Row(),
1971 nTab, nDelFlag );
1972}
1973
1974void ScDocument::InitUndoSelected(const ScDocument& rSrcDoc, const ScMarkData& rTabSelection,
1975 bool bColInfo, bool bRowInfo )
1976{
1977 if (bIsUndo)
1978 {
1979 Clear();
1980
1981 SharePooledResources(&rSrcDoc);
1982
1983 for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++)
1984 if ( rTabSelection.GetTableSelect( nTab ) )
1985 {
1986 ScTableUniquePtr pTable(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
1987 if (nTab < static_cast<SCTAB>(maTabs.size()))
1988 maTabs[nTab] = std::move(pTable);
1989 else
1990 maTabs.push_back(std::move(pTable));
1991 }
1992 else
1993 {
1994 if (nTab < static_cast<SCTAB>(maTabs.size()))
1995 maTabs[nTab]=nullptr;
1996 else
1997 maTabs.push_back(nullptr);
1998 }
1999 }
2000 else
2001 {
2002 OSL_FAIL("InitUndo");
2003 }
2004}
2005
2006void ScDocument::InitUndo( const ScDocument& rSrcDoc, SCTAB nTab1, SCTAB nTab2,
2007 bool bColInfo, bool bRowInfo )
2008{
2009 if (!bIsUndo)
2010 {
2011 OSL_FAIL("InitUndo");
2012 return;
2013 }
2014
2015 Clear();
2016
2017 // Undo document shares its pooled resources with the source document.
2018 SharePooledResources(&rSrcDoc);
2019
2020 if (rSrcDoc.mpShell->GetMedium())
2022
2023 if ( nTab2 >= static_cast<SCTAB>(maTabs.size()))
2024 maTabs.resize(nTab2 + 1);
2025 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
2026 {
2027 maTabs[nTab].reset(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
2028 }
2029}
2030
2031void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo )
2032{
2033 if (!bIsUndo)
2034 {
2035 OSL_FAIL("AddUndoTab");
2036 return;
2037 }
2038
2039 if (nTab2 >= static_cast<SCTAB>(maTabs.size()))
2040 {
2041 maTabs.resize(nTab2+1);
2042 }
2043
2044 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
2045 if (!maTabs[nTab])
2046 {
2047 maTabs[nTab].reset( new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo) );
2048 }
2049}
2050
2051void ScDocument::SetCutMode( bool bVal )
2052{
2053 if (bIsClip)
2054 GetClipParam().mbCutMode = bVal;
2055 else
2056 {
2057 OSL_FAIL("SetCutMode without bIsClip");
2058 }
2059}
2060
2062{
2063 if (bIsClip)
2064 return GetClipParam().mbCutMode;
2065 else
2066 {
2067 OSL_FAIL("IsCutMode without bIsClip");
2068 return false;
2069 }
2070}
2071
2073 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2074 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2075 const ScMarkData* pMarks, bool bColRowFlags )
2076{
2077 if (ValidTab(nTab1) && ValidTab(nTab2))
2078 {
2079 ScRange aThisRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2080 CopyToDocument(aThisRange, nFlags, bOnlyMarked, rDestDoc, pMarks, bColRowFlags);
2081 }
2082}
2083
2085 SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
2086 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2087{
2088 PutInOrder( nCol1, nCol2 );
2089 PutInOrder( nRow1, nRow2 );
2090 PutInOrder( nTab1, nTab2 );
2091 if (!(ValidTab(nTab1) && ValidTab(nTab2)))
2092 return;
2093
2094 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2095
2096 if (nTab1 > 0)
2097 CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2098
2099 sc::CopyToDocContext aCxt(rDestDoc);
2100 assert( nTab2 < static_cast<SCTAB>(maTabs.size()) && nTab2 < static_cast<SCTAB>(rDestDoc.maTabs.size()));
2101 for (SCTAB i = nTab1; i <= nTab2; i++)
2102 {
2103 if (maTabs[i] && rDestDoc.maTabs[i])
2104 maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
2105 bOnlyMarked, rDestDoc.maTabs[i].get());
2106 }
2107
2108 if (nTab2 < MAXTAB)
2109 CopyToDocument(0, 0, nTab2+1, MaxCol(), MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc);
2110}
2111
2113 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
2114 const ScMarkData* pMarks, bool bColRowFlags)
2115{
2116 ScRange aNewRange = rRange;
2117 aNewRange.PutInOrder();
2118
2119 if (rDestDoc.aDocName.isEmpty())
2120 rDestDoc.aDocName = aDocName;
2121
2122 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
2123 ScBulkBroadcast aBulkBroadcast(rDestDoc.GetBASM(), SfxHintId::ScDataChanged);
2124 sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
2125
2126 sc::CopyToDocContext aCxt(rDestDoc);
2127 aCxt.setStartListening(false);
2128
2129 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
2130 for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++)
2131 {
2132 ScTable* pTab = FetchTable(i);
2133 ScTable* pDestTab = rDestDoc.FetchTable(i);
2134 if (!pTab || !pDestTab)
2135 continue;
2136
2137 pTab->CopyToTable(
2138 aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2139 nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags,
2140 /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true);
2141 }
2142
2143 rDestDoc.StartAllListeners(aNewRange);
2144}
2145
2147 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
2148{
2149 sc::AutoCalcSwitch aAutoCalcSwitch(*this, false);
2150
2151 ScRange aNewRange = rRange;
2152 aNewRange.PutInOrder();
2153 SCTAB nTab1 = aNewRange.aStart.Tab();
2154 SCTAB nTab2 = aNewRange.aEnd.Tab();
2155
2156 sc::CopyToDocContext aCxt(rDestDoc);
2157 if (nTab1 > 0)
2158 CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
2159
2160 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
2161 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
2162 {
2163 if (maTabs[i] && rDestDoc.maTabs[i])
2164 maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(),
2165 aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
2166 nFlags, bOnlyMarked, rDestDoc.maTabs[i].get());
2167 }
2168
2169 if (nTab2 < static_cast<SCTAB>(maTabs.size()))
2170 CopyToDocument(0, 0 , nTab2+1, MaxCol(), MaxRow(), maTabs.size(), InsertDeleteFlags::FORMULA, false, rDestDoc);
2171}
2172
2174 ScDocument* pClipDoc, const ScMarkData* pMarks,
2175 bool bKeepScenarioFlags, bool bIncludeObjects )
2176{
2177 OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" );
2178
2179 if (bIsClip)
2180 return;
2181
2182 if (!pClipDoc)
2183 {
2184 SAL_WARN("sc", "CopyToClip: no ClipDoc");
2185 pClipDoc = ScModule::GetClipDoc();
2186 }
2187
2188 if (mpShell->GetMedium())
2189 {
2191 // for unsaved files use the title name and adjust during save of file
2192 if (pClipDoc->maFileURL.isEmpty())
2193 pClipDoc->maFileURL = mpShell->GetName();
2194 }
2195 else
2196 {
2197 pClipDoc->maFileURL = mpShell->GetName();
2198 }
2199
2200 //init maTabNames
2201 for (const auto& rxTab : maTabs)
2202 {
2203 if( rxTab )
2204 {
2205 OUString aTabName = rxTab->GetName();
2206 pClipDoc->maTabNames.push_back(aTabName);
2207 }
2208 else
2209 pClipDoc->maTabNames.emplace_back();
2210 }
2211
2212 pClipDoc->aDocName = aDocName;
2213 pClipDoc->SetClipParam(rClipParam);
2214 ScRange aClipRange = rClipParam.getWholeRange();
2215 SCTAB nEndTab = static_cast<SCTAB>(maTabs.size());
2216
2217 pClipDoc->ResetClip(this, pMarks);
2218
2219 sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
2220 CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);
2221
2222 for (SCTAB i = 0; i < nEndTab; ++i)
2223 {
2224 if (!maTabs[i] || i >= static_cast<SCTAB>(pClipDoc->maTabs.size()) || !pClipDoc->maTabs[i])
2225 continue;
2226
2227 if ( pMarks && !pMarks->GetTableSelect(i) )
2228 continue;
2229
2230 maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
2231
2232 if (mpDrawLayer && bIncludeObjects)
2233 {
2234 // also copy drawing objects
2235 tools::Rectangle aObjRect = GetMMRect(
2236 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
2237 mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
2238 }
2239 }
2240
2241 // Make sure to mark overlapped cells.
2242 pClipDoc->ExtendMerge(aClipRange, true);
2243}
2244
2245void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument& rDestDoc)
2246{
2247 ScTable* pSrcTab = rSrcRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr;
2248 ScTable* pDestTab = nDestTab < static_cast<SCTAB>(rDestDoc.maTabs.size()) ? rDestDoc.maTabs[nDestTab].get() : nullptr;
2249
2250 if (!pSrcTab || !pDestTab)
2251 return;
2252
2255
2256 pSrcTab->CopyStaticToDocument(
2257 rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(),
2258 aMap, pDestTab);
2259}
2260
2261void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc )
2262{
2263 if (!TableExists(rSrcPos.Tab()) || !rDestDoc.TableExists(rDestPos.Tab()))
2264 return;
2265
2266 ScTable& rSrcTab = *maTabs[rSrcPos.Tab()];
2267 ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()];
2268
2269 rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab);
2270}
2271
2273 SCCOL nCol2, SCROW nRow2,
2274 SCTAB nTab, ScDocument* pClipDoc)
2275{
2276 if (bIsClip)
2277 return;
2278
2279 if (!pClipDoc)
2280 {
2281 SAL_WARN("sc", "CopyTabToClip: no ClipDoc");
2282 pClipDoc = ScModule::GetClipDoc();
2283 }
2284
2285 if (mpShell->GetMedium())
2286 {
2288 // for unsaved files use the title name and adjust during save of file
2289 if (pClipDoc->maFileURL.isEmpty())
2290 pClipDoc->maFileURL = mpShell->GetName();
2291 }
2292 else
2293 {
2294 pClipDoc->maFileURL = mpShell->GetName();
2295 }
2296
2297 //init maTabNames
2298 for (const auto& rxTab : maTabs)
2299 {
2300 if( rxTab )
2301 {
2302 OUString aTabName = rxTab->GetName();
2303 pClipDoc->maTabNames.push_back(aTabName);
2304 }
2305 else
2306 pClipDoc->maTabNames.emplace_back();
2307 }
2308
2309 PutInOrder( nCol1, nCol2 );
2310 PutInOrder( nRow1, nRow2 );
2311
2312 ScClipParam& rClipParam = pClipDoc->GetClipParam();
2313 pClipDoc->aDocName = aDocName;
2314 rClipParam.maRanges.RemoveAll();
2315 rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
2316 pClipDoc->ResetClip( this, nTab );
2317
2318 sc::CopyToClipContext aCxt(*pClipDoc, false);
2319 if (nTab < static_cast<SCTAB>(maTabs.size()) && nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()))
2320 if (maTabs[nTab] && pClipDoc->maTabs[nTab])
2321 maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get());
2322
2323 pClipDoc->GetClipParam().mbCutMode = false;
2324}
2325
2326void ScDocument::TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
2327 bool bIncludeFiltered)
2328{
2329 OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip,
2330 "TransposeClip with wrong Document" );
2331
2332 // initialize
2333 // -> pTransClip has to be deleted before the original document!
2334
2335 pTransClip->ResetClip(this, nullptr); // all
2336
2337 // Take over range
2338
2339 if (pRangeName)
2340 {
2341 pTransClip->GetRangeName()->clear();
2342 for (const auto& rEntry : *pRangeName)
2343 {
2344 sal_uInt16 nIndex = rEntry.second->GetIndex();
2345 ScRangeData* pData = new ScRangeData(*rEntry.second);
2346 if (pTransClip->pRangeName->insert(pData))
2347 pData->SetIndex(nIndex);
2348 }
2349 }
2350
2351 ScRange aCombinedClipRange = GetClipParam().getWholeRange();
2352
2353 if (!ValidRow(aCombinedClipRange.aEnd.Row() - aCombinedClipRange.aStart.Row()))
2354 {
2355 SAL_WARN("sc", "TransposeClip: Too big");
2356 return;
2357 }
2358
2359 // Transpose of filtered multi range row selection is a special case since filtering
2360 // and selection are in the same dimension (i.e. row).
2361 // The filtered row status and the selection ranges are not available at the same time,
2362 // handle this case specially, do not use GetClipParam().getWholeRange(),
2363 // instead loop through the ranges, calculate the row offset and handle filtered rows and
2364 // create in ScClipParam::transpose() a unified range.
2365 const bool bIsMultiRangeRowFilteredTranspose
2366 = !bIncludeFiltered && GetClipParam().isMultiRange()
2367 && HasFilteredRows(aCombinedClipRange.aStart.Row(), aCombinedClipRange.aEnd.Row(),
2368 aCombinedClipRange.aStart.Tab())
2370
2371 ScRangeList aClipRanges;
2372 if (bIsMultiRangeRowFilteredTranspose)
2373 aClipRanges = GetClipParam().maRanges;
2374 else
2375 aClipRanges = ScRangeList(aCombinedClipRange);
2376
2377 // The data
2378 ScRange aClipRange;
2379 SCROW nRowCount = 0; // next consecutive row
2380 for (size_t j = 0, n = aClipRanges.size(); j < n; ++j)
2381 {
2382 aClipRange = aClipRanges[j];
2383
2384 SCROW nRowOffset = 0;
2385 if (bIsMultiRangeRowFilteredTranspose)
2386 {
2387 // adjust for the rows that are filtered
2388 nRowOffset = nRowCount;
2389
2390 // calculate filtered rows of current clip range
2391 SCROW nRowCountNonFiltered = CountNonFilteredRows(
2392 aClipRange.aStart.Row(), aClipRange.aEnd.Row(), aClipRange.aStart.Tab());
2393 assert(!bIncludeFiltered && "bIsMultiRangeRowFilteredTranspose can only be true if bIncludeFiltered is false");
2394 nRowCount += nRowCountNonFiltered; // for next iteration
2395 }
2396
2397 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
2398 {
2399 if (maTabs[i])
2400 {
2401 OSL_ENSURE(pTransClip->maTabs[i], "TransposeClip: Table not there");
2402 maTabs[i]->TransposeClip(
2403 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(),
2404 aClipRange.aEnd.Row(), aCombinedClipRange.aStart.Row(), nRowOffset,
2405 pTransClip->maTabs[i].get(), nFlags, bAsLink, bIncludeFiltered);
2406
2407 if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) )
2408 {
2409 // Drawing objects are copied to the new area without transposing.
2410 // CopyFromClip is used to adjust the objects to the transposed block's
2411 // cell range area.
2412 // (mpDrawLayer in the original clipboard document is set only if there
2413 // are drawing objects to copy)
2414
2415 pTransClip->InitDrawLayer();
2416 tools::Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(),
2417 aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i );
2418 tools::Rectangle aDestRect = pTransClip->GetMMRect( 0, 0,
2419 static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()),
2420 static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i );
2421 pTransClip->mpDrawLayer->CopyFromClip( mpDrawLayer.get(), i, aSourceRect, ScAddress(0,0,i), aDestRect );
2422 }
2423 }
2424 }
2425 }
2426
2427 pTransClip->SetClipParam(GetClipParam());
2428 pTransClip->GetClipParam().transpose(*this, bIncludeFiltered,
2429 bIsMultiRangeRowFilteredTranspose);
2430
2431 // This happens only when inserting...
2432
2433 GetClipParam().mbCutMode = false;
2434}
2435
2436namespace {
2437
2438void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName,
2440{
2441 pClipRangeName->clear();
2442 for (const auto& rEntry : *pRangeName) //TODO: also DB and Pivot regions!!!
2443 {
2444 sal_uInt16 nIndex = rEntry.second->GetIndex();
2445 bool bInUse = (rUsedNames.count(nIndex) > 0);
2446 if (!bInUse)
2447 continue;
2448
2449 ScRangeData* pData = new ScRangeData(*rEntry.second);
2450 if (pClipRangeName->insert(pData))
2451 pData->SetIndex(nIndex);
2452 }
2453}
2454
2455}
2456
2457void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks)
2458{
2459 if (!pRangeName || pRangeName->empty())
2460 return;
2461
2462 sc::UpdatedRangeNames aUsedNames; // indexes of named ranges that are used in the copied cells
2463 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pClipDoc->maTabs.size()));
2464 for (SCTAB i = 0; i < nMinSizeBothTabs; ++i)
2465 if (maTabs[i] && pClipDoc->maTabs[i])
2466 if ( !pMarks || pMarks->GetTableSelect(i) )
2467 maTabs[i]->FindRangeNamesInUse(
2468 rClipRange.aStart.Col(), rClipRange.aStart.Row(),
2469 rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames);
2470
2471 /* TODO: handle also sheet-local names */
2472 sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1));
2473 copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames);
2474}
2475
2477 : mrDoc(rDoc)
2478{
2479 mrDoc.MergeNumberFormatter(rSrcDoc);
2480}
2481
2483{
2485 mrDoc.pFormatExchangeList = nullptr;
2486}
2487
2489{
2491 mpFormulaGroupCxt.reset();
2492}
2493
2495{
2496 ScTable* pTab = FetchTable(rPos.Tab());
2497 if (!pTab)
2498 return nullptr;
2499
2500 return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2501}
2502
2504{
2505 const ScTable* pTab = FetchTable(rPos.Tab());
2506 if (!pTab)
2507 return nullptr;
2508
2509 return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
2510}
2511
2513{
2514 ScTable* pTab = FetchTable(rTopPos.Tab());
2515 if (!pTab || nLength <= 0)
2516 return;
2517
2518 pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
2519}
2520
2521#if DUMP_COLUMN_STORAGE
2522void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
2523{
2524 const ScTable* pTab = FetchTable(nTab);
2525 if (!pTab)
2526 return;
2527
2528 pTab->DumpColumnStorage(nCol);
2529}
2530#endif
2531
2532#if DEBUG_AREA_BROADCASTER
2533void ScDocument::DumpAreaBroadcasters() const
2534{
2535 if (pBASM)
2536 pBASM->Dump();
2537}
2538#endif
2539
2541{
2542 return ValidTab(nTab) && o3tl::make_unsigned(nTab) < maTabs.size() && maTabs[nTab];
2543}
2544
2546{
2547 if (!TableExists(nTab))
2548 return nullptr;
2549
2550 return maTabs[nTab].get();
2551}
2552
2554{
2555 if (!TableExists(nTab))
2556 return nullptr;
2557
2558 return maTabs[nTab].get();
2559}
2560
2562{
2563 if (!TableExists(nTab))
2564 {
2565 SAL_WARN("sc", "GetWritableColumnsRange() called for non-existent table");
2566 return ScColumnsRange(-1, -1);
2567 }
2568 return maTabs[nTab]->GetWritableColumnsRange(nColBegin, nColEnd);
2569}
2570
2572{
2573 if (!TableExists(nTab))
2574 return ScColumnsRange(-1, -1);
2575 return maTabs[nTab]->GetAllocatedColumnsRange(nColBegin, nColEnd);
2576}
2577
2579{
2580 if (!TableExists(nTab))
2581 return ScColumnsRange(-1, -1);
2582 return maTabs[nTab]->GetColumnsRange(nColBegin, nColEnd);
2583}
2584
2586{
2587 SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable();
2588 SvNumberFormatter* pOtherFormatter = rSrcDoc.mxPoolHelper->GetFormTable();
2589 if (pOtherFormatter && pOtherFormatter != pThisFormatter)
2590 {
2591 SvNumberFormatterIndexTable* pExchangeList =
2592 pThisFormatter->MergeFormatter(*pOtherFormatter);
2593 if (!pExchangeList->empty())
2594 {
2596 pFormatExchangeList = pExchangeList;
2597 }
2598 }
2599}
2600
2602{
2603 if (!mpClipParam)
2604 mpClipParam.reset(new ScClipParam);
2605
2606 return *mpClipParam;
2607}
2608
2610{
2611 mpClipParam.reset(new ScClipParam(rParam));
2612}
2613
2615{
2616 if (bIsClip || mpShell == nullptr || mpShell->IsLoading())
2617 return false;
2618
2619 ScDocument* pClipDoc = ScModule::GetClipDoc();
2620 return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() &&
2621 mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
2622}
2623
2625 SCCOL nCol2, SCROW nRow2,
2626 const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
2627{
2628 if (!(nInsFlag & InsertDeleteFlags::CONTENTS))
2629 return;
2630
2631 auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
2632
2633 sc::StartListeningContext aStartCxt(*this, pSet);
2634 sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
2635
2636 SCTAB nMax = static_cast<SCTAB>(maTabs.size());
2637 for (const auto& rTab : rMark)
2638 {
2639 if (rTab >= nMax)
2640 break;
2641 if (maTabs[rTab])
2642 maTabs[rTab]->StartListeningFormulaCells(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2);
2643 }
2644}
2645
2647 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
2648 InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
2649{
2650 if (nInsFlag & InsertDeleteFlags::CONTENTS)
2651 {
2652 SCTAB nMax = static_cast<SCTAB>(maTabs.size());
2653 for (const auto& rTab : rMark)
2654 {
2655 if (rTab >= nMax)
2656 break;
2657 if (maTabs[rTab])
2658 maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
2659 }
2660 }
2661}
2662
2664{
2665 if (!TableExists(nTab))
2666 return false;
2667
2668 return maTabs[nTab]->InitColumnBlockPosition(rBlockPos, nCol);
2669}
2670
2672 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
2673 const ScMarkData& rMark, SCCOL nDx, SCROW nDy )
2674{
2675 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2676 SCTAB nTabEnd = rCxt.getTabEnd();
2677 SCTAB nClipTab = 0;
2678 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
2679 {
2680 if (maTabs[i] && rMark.GetTableSelect(i) )
2681 {
2682 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2683
2684 maTabs[i]->CopyFromClip(
2685 rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get());
2686
2688 {
2689 // also copy drawing objects
2690
2691 // drawing layer must be created before calling CopyFromClip
2692 // (ScDocShell::MakeDrawLayer also does InitItems etc.)
2693 OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" );
2694 if ( mpDrawLayer )
2695 {
2696 // For GetMMRect, the row heights in the target document must already be valid
2697 // (copied in an extra step before pasting, or updated after pasting cells, but
2698 // before pasting objects).
2699
2700 tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect(
2701 nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab );
2702 tools::Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i );
2703 mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRect,
2704 ScAddress( nCol1, nRow1, i ), aDestRect );
2705 }
2706 }
2707
2708 nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2709 }
2710 }
2712 return;
2713
2714 nClipTab = 0;
2715 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
2716 {
2717 if (maTabs[i] && rMark.GetTableSelect(i) )
2718 {
2719 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
2720 SCTAB nDz = i - nClipTab;
2721
2722 // ranges of consecutive selected tables (in clipboard and dest. doc)
2723 // must be handled in one UpdateReference call
2724 SCTAB nFollow = 0;
2725 while ( i + nFollow < nTabEnd
2726 && rMark.GetTableSelect( i + nFollow + 1 )
2727 && nClipTab + nFollow < MAXTAB
2728 && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
2729 ++nFollow;
2730
2731 sc::RefUpdateContext aRefCxt(*this, rCxt.getClipDoc());
2732 aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
2733 aRefCxt.mnColDelta = nDx;
2734 aRefCxt.mnRowDelta = nDy;
2735 aRefCxt.mnTabDelta = nDz;
2736 aRefCxt.setBlockPositionReference(rCxt.getBlockPositionSet()); // share mdds position caching
2737 if (rCxt.getClipDoc()->GetClipParam().mbCutMode)
2738 {
2739 // Update references only if cut originates from the same
2740 // document we are pasting into.
2741 if (rCxt.getClipDoc()->GetPool() == GetPool())
2742 {
2743 bool bOldInserting = IsInsertingFromOtherDoc();
2745 aRefCxt.meMode = URM_MOVE;
2746 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2747
2748 // For URM_MOVE group listeners may have been removed,
2749 // re-establish them.
2750 if (!aRefCxt.maRegroupCols.empty())
2751 {
2752 /* TODO: holding the ColumnSet in a shared_ptr at
2753 * RefUpdateContext would eliminate the need of
2754 * copying it here. */
2755 auto pColSet = std::make_shared<sc::ColumnSet>( aRefCxt.maRegroupCols);
2756 StartNeededListeners( pColSet);
2757 }
2758
2759 SetInsertingFromOtherDoc( bOldInserting);
2760 }
2761 }
2762 else
2763 {
2764 aRefCxt.meMode = URM_COPY;
2765 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
2766 }
2767
2768 nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size());
2769 i = sal::static_int_cast<SCTAB>( i + nFollow );
2770 }
2771 }
2772}
2773
2775 SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
2776 SCCOL nDx, SCROW& rClipStartRow, SCROW nClipEndRow)
2777{
2778 // call CopyBlockFromClip for ranges of consecutive non-filtered rows
2779 // nCol1/nRow1 etc. is in target doc
2780
2781 // filtered state is taken from first used table in clipboard (as in GetClipArea)
2782 SCTAB nFlagTab = 0;
2783 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
2784 while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] )
2785 ++nFlagTab;
2786
2787 SCROW nSourceRow = rClipStartRow;
2788 SCROW nSourceEnd = nClipEndRow;
2789 SCROW nDestRow = nRow1;
2790 SCROW nFilteredRows = 0;
2791
2792 while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 )
2793 {
2794 // skip filtered rows
2795 SCROW nSourceRowOriginal = nSourceRow;
2796 nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab);
2797 nFilteredRows += nSourceRow - nSourceRowOriginal;
2798
2799 if ( nSourceRow <= nSourceEnd )
2800 {
2801 // look for more non-filtered rows following
2802 SCROW nLastRow = nSourceRow;
2803 (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow);
2804 SCROW nFollow = nLastRow - nSourceRow;
2805
2806 if (nFollow > nSourceEnd - nSourceRow)
2807 nFollow = nSourceEnd - nSourceRow;
2808 if (nFollow > nRow2 - nDestRow)
2809 nFollow = nRow2 - nDestRow;
2810
2811 SCROW nNewDy = nDestRow - nSourceRow;
2813 rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy);
2814
2815 nSourceRow += nFollow + 1;
2816 nDestRow += nFollow + 1;
2817 }
2818 }
2819 rClipStartRow = nSourceRow;
2820 return nFilteredRows;
2821}
2822
2823namespace {
2824
2825class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
2826{
2827 ScDocument& mrDoc;
2828 ScColumn* mpCol;
2829
2830public:
2831 explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {}
2832
2833 virtual void startColumn( ScColumn* pCol ) override
2834 {
2835 mpCol = pCol;
2836 }
2837
2838 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
2839 {
2840 if (!bVal)
2841 return;
2842
2843 assert(mpCol);
2844 ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
2845 aRange.aEnd.SetRow(nRow2);
2846 mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
2847 };
2848};
2849
2850}
2851
2853 const ScRange& rDestRange, const ScMarkData& rMark, InsertDeleteFlags nInsFlag,
2854 ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut,
2855 bool bAsLink, bool bIncludeFiltered, bool bSkipEmptyCells,
2856 const ScRangeList * pDestRanges )
2857{
2858 if (bIsClip)
2859 return;
2860
2861 if (!pClipDoc)
2862 {
2863 OSL_FAIL("CopyFromClip: no ClipDoc");
2864 pClipDoc = ScModule::GetClipDoc();
2865 }
2866
2867 if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
2868 return;
2869
2870 sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc.
2871
2872 NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
2873
2874 SCCOL nAllCol1 = rDestRange.aStart.Col();
2875 SCROW nAllRow1 = rDestRange.aStart.Row();
2876 SCCOL nAllCol2 = rDestRange.aEnd.Col();
2877 SCROW nAllRow2 = rDestRange.aEnd.Row();
2878
2879 SCCOL nXw = 0;
2880 SCROW nYw = 0;
2881 ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
2882 for (SCTAB nTab = 0; nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()); nTab++) // find largest merge overlap
2883 if (pClipDoc->maTabs[nTab]) // all sheets of the clipboard content
2884 {
2885 SCCOL nThisEndX = aClipRange.aEnd.Col();
2886 SCROW nThisEndY = aClipRange.aEnd.Row();
2887 pClipDoc->ExtendMerge( aClipRange.aStart.Col(),
2888 aClipRange.aStart.Row(),
2889 nThisEndX, nThisEndY, nTab );
2890 // only extra value from ExtendMerge
2891 nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() );
2892 nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() );
2893 if ( nThisEndX > nXw )
2894 nXw = nThisEndX;
2895 if ( nThisEndY > nYw )
2896 nYw = nThisEndY;
2897 }
2898
2899 SCCOL nDestAddX;
2900 SCROW nDestAddY;
2901 pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered );
2902 nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX );
2903 nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value
2904
2905 /* Decide which contents to delete before copying. Delete all
2906 contents if nInsFlag contains any real content flag.
2907 #i102056# Notes are pasted from clipboard in a second pass,
2908 together with the special flag InsertDeleteFlags::ADDNOTES that states to not
2909 overwrite/delete existing cells but to insert the notes into
2910 these cells. In this case, just delete old notes from the
2911 destination area. */
2914 nDelFlag |= InsertDeleteFlags::NOTE;
2915 else if ( nInsFlag & InsertDeleteFlags::CONTENTS )
2916 nDelFlag |= InsertDeleteFlags::CONTENTS;
2917
2918 if (nInsFlag & InsertDeleteFlags::ATTRIB)
2919 nDelFlag |= InsertDeleteFlags::ATTRIB;
2920
2921 sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipEmptyCells);
2922 std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
2923 aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
2924 aCxt.setDeleteFlag(nDelFlag);
2925
2926 ScRangeList aLocalRangeList;
2927 if (!pDestRanges)
2928 {
2929 aLocalRangeList.push_back( rDestRange);
2930 pDestRanges = &aLocalRangeList;
2931 }
2932
2933 bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
2934
2935 sc::ColumnSpanSet aBroadcastSpans;
2936
2937 SCCOL nClipStartCol = aClipRange.aStart.Col();
2938 SCROW nClipStartRow = aClipRange.aStart.Row();
2939 SCROW nClipEndRow = aClipRange.aEnd.Row();
2940 for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange )
2941 {
2942 const ScRange & rRange = (*pDestRanges)[nRange];
2943 SCCOL nCol1 = rRange.aStart.Col();
2944 SCROW nRow1 = rRange.aStart.Row();
2945 SCCOL nCol2 = rRange.aEnd.Col();
2946 SCROW nRow2 = rRange.aEnd.Row();
2947
2948 aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
2949 DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans);
2950
2951 if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
2952 continue;
2953
2954 SCCOL nC1 = nCol1;
2955 SCROW nR1 = nRow1;
2956 SCCOL nC2 = nC1 + nXw;
2957 if (nC2 > nCol2)
2958 nC2 = nCol2;
2959 SCROW nR2 = nR1 + nYw;
2960 if (nR2 > nRow2)
2961 nR2 = nRow2;
2962
2963 const SCCOLROW nThreshold = 8192;
2964 bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold));
2965 std::vector< SCTAB > vTables;
2966
2967 if (bPreallocatePattern)
2968 {
2969 for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i)
2970 if (maTabs[i] && rMark.GetTableSelect( i ) )
2971 vTables.push_back( i );
2972 }
2973
2974 do
2975 {
2976 // Pasting is done column-wise, when pasting to a filtered
2977 // area this results in partitioning and we have to
2978 // remember and reset the start row for each column until
2979 // it can be advanced for the next chunk of unfiltered
2980 // rows.
2981 SCROW nSaveClipStartRow = nClipStartRow;
2982 do
2983 {
2984 nClipStartRow = nSaveClipStartRow;
2985 SCCOL nDx = nC1 - nClipStartCol;
2986 SCROW nDy = nR1 - nClipStartRow;
2987 if ( bIncludeFiltered )
2988 {
2990 aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy);
2991 nClipStartRow += nR2 - nR1 + 1;
2992 }
2993 else
2994 {
2995 CopyNonFilteredFromClip(aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow,
2996 nClipEndRow);
2997 }
2998 nC1 = nC2 + 1;
2999 nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2);
3000 } while (nC1 <= nCol2);
3001 if (nClipStartRow > nClipEndRow)
3002 nClipStartRow = aClipRange.aStart.Row();
3003 nC1 = nCol1;
3004 nC2 = nC1 + nXw;
3005 if (nC2 > nCol2)
3006 nC2 = nCol2;
3007
3008 // Preallocate pattern memory once if further chunks are to be pasted.
3009 if (bPreallocatePattern && (nR2+1) <= nRow2)
3010 {
3011 SCROW nR3 = nR2 + 1;
3012 for (SCTAB nTab : vTables)
3013 {
3014 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
3015 {
3016 // Pattern count of the first chunk pasted.
3017 SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2);
3018 // If it is only one pattern per chunk and chunks are
3019 // pasted consecutively then it will get its range
3020 // enlarged for each chunk and no further allocation
3021 // happens. For non-consecutive chunks we're out of
3022 // luck in this case.
3023 if (nChunk > 1)
3024 {
3025 SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1);
3026 SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2);
3027 if (nNeeded > nRemain)
3028 {
3029 SCSIZE nCurr = GetPatternCount( nTab, nCol);
3030 ReservePatternCount( nTab, nCol, nCurr + nNeeded);
3031 }
3032 }
3033 }
3034 }
3035 bPreallocatePattern = false;
3036 }
3037
3038 nR1 = nR2 + 1;
3039 nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2);
3040 } while (nR1 <= nRow2);
3041 }
3042
3043 bInsertingFromOtherDoc = false;
3044
3045 // Create Listener after everything has been inserted
3046 StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag );
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>(