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