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 <svx/sdrundomanager.hxx> 25 #include <svx/svditer.hxx> 26 #include <sfx2/objsh.hxx> 27 #include <sfx2/viewsh.hxx> 28 #include <sfx2/docfile.hxx> 29 #include <svl/poolcach.hxx> 30 #include <svl/zforlist.hxx> 31 #include <unotools/charclass.hxx> 32 #include <unotools/transliterationwrapper.hxx> 33 #include <tools/urlobj.hxx> 34 #include <sal/log.hxx> 35 36 #include <com/sun/star/text/WritingMode2.hpp> 37 #include <com/sun/star/script/vba/XVBACompatibility.hpp> 38 #include <com/sun/star/sheet/TablePageBreakData.hpp> 39 #include <com/sun/star/lang/NotInitializedException.hpp> 40 41 #include <document.hxx> 42 #include <table.hxx> 43 #include <column.hxx> 44 #include <attrib.hxx> 45 #include <attarray.hxx> 46 #include <patattr.hxx> 47 #include <rangenam.hxx> 48 #include <poolhelp.hxx> 49 #include <docpool.hxx> 50 #include <stlpool.hxx> 51 #include <stlsheet.hxx> 52 #include <globstr.hrc> 53 #include <scresid.hxx> 54 #include <dbdata.hxx> 55 #include <chartlis.hxx> 56 #include <rangelst.hxx> 57 #include <markdata.hxx> 58 #include <drwlayer.hxx> 59 #include <validat.hxx> 60 #include <prnsave.hxx> 61 #include <chgtrack.hxx> 62 #include <hints.hxx> 63 #include <detdata.hxx> 64 #include <dpobject.hxx> 65 #include <detfunc.hxx> 66 #include <scmod.hxx> 67 #include <dociter.hxx> 68 #include <progress.hxx> 69 #include <autonamecache.hxx> 70 #include <bcaslot.hxx> 71 #include <postit.hxx> 72 #include <clipparam.hxx> 73 #include <defaultsoptions.hxx> 74 #include <editutil.hxx> 75 #include <stringutil.hxx> 76 #include <formulaiter.hxx> 77 #include <formulacell.hxx> 78 #include <clipcontext.hxx> 79 #include <listenercontext.hxx> 80 #include <scopetools.hxx> 81 #include <refupdatecontext.hxx> 82 #include <formulagroup.hxx> 83 #include <tokenstringcontext.hxx> 84 #include <compressedarray.hxx> 85 #include <docsh.hxx> 86 #include <brdcst.hxx> 87 88 #include <formula/vectortoken.hxx> 89 90 #include <limits> 91 #include <memory> 92 #include <utility> 93 94 #include <comphelper/lok.hxx> 95 #include <LibreOfficeKit/LibreOfficeKitEnums.h> 96 97 #include <mtvelements.hxx> 98 99 using ::editeng::SvxBorderLine; 100 using namespace ::com::sun::star; 101 102 namespace WritingMode2 = ::com::sun::star::text::WritingMode2; 103 using ::com::sun::star::uno::Sequence; 104 using ::com::sun::star::sheet::TablePageBreakData; 105 using ::std::set; 106 107 namespace { 108 109 std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark) 110 { 111 SCTAB nTabStart = MAXTAB; 112 SCTAB nTabEnd = 0; 113 SCTAB nMax = static_cast<SCTAB>(rTables.size()); 114 for (const auto& rTab : rMark) 115 { 116 if (rTab >= nMax) 117 break; 118 119 if (!rTables[rTab]) 120 continue; 121 122 if (rTab < nTabStart) 123 nTabStart = rTab; 124 nTabEnd = rTab; 125 } 126 127 return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd); 128 } 129 130 } 131 132 struct ScDefaultAttr 133 { 134 const ScPatternAttr* pAttr; 135 SCROW nFirst; 136 SCSIZE nCount; 137 explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {} 138 }; 139 140 struct ScLessDefaultAttr 141 { 142 bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const 143 { 144 return rValue1.pAttr < rValue2.pAttr; 145 } 146 }; 147 148 typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet; 149 150 void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck ) 151 { 152 if ( ValidTab(nTab) && ( nTab >= static_cast<SCTAB>(maTabs.size()) ||!maTabs[nTab]) ) 153 { 154 // Get Custom prefix 155 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions(); 156 OUString aString = rOpt.GetInitTabPrefix(); 157 158 aString += OUString::number(nTab+1); 159 if ( _bNeedsNameCheck ) 160 CreateValidTabName( aString ); // no doubles 161 if (nTab < static_cast<SCTAB>(maTabs.size())) 162 { 163 maTabs[nTab].reset( new ScTable(this, nTab, aString) ); 164 } 165 else 166 { 167 while(nTab > static_cast<SCTAB>(maTabs.size())) 168 maTabs.push_back(nullptr); 169 maTabs.emplace_back( new ScTable(this, nTab, aString) ); 170 } 171 maTabs[nTab]->SetLoadingMedium(bLoadingMedium); 172 } 173 } 174 175 bool ScDocument::HasTable( SCTAB nTab ) const 176 { 177 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 178 if (maTabs[nTab]) 179 return true; 180 181 return false; 182 } 183 184 bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const 185 { 186 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 187 { 188 if (maTabs[nTab]) 189 { 190 rHashCode = maTabs[nTab]->GetHashCode(); 191 return true; 192 } 193 } 194 return false; 195 } 196 197 bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const 198 { 199 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 200 { 201 if (maTabs[nTab]) 202 { 203 rName = maTabs[nTab]->GetName(); 204 return true; 205 } 206 } 207 rName.clear(); 208 return false; 209 } 210 211 OUString ScDocument::GetCopyTabName( SCTAB nTab ) const 212 { 213 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size())) 214 return maTabNames[nTab]; 215 return OUString(); 216 } 217 218 bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName ) 219 { 220 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 221 { 222 if (maTabs[nTab]) 223 { 224 maTabs[nTab]->SetCodeName( rName ); 225 return true; 226 } 227 } 228 SAL_WARN("sc", "can't set code name " << rName ); 229 return false; 230 } 231 232 bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const 233 { 234 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 235 if (maTabs[nTab]) 236 { 237 rName = maTabs[nTab]->GetCodeName(); 238 return true; 239 } 240 rName.clear(); 241 return false; 242 } 243 244 bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const 245 { 246 OUString aUpperName; 247 static OUString aCacheName, aCacheUpperName; 248 249 assert(!IsThreadedGroupCalcInProgress()); 250 if (aCacheName != rName) 251 { 252 aCacheName = rName; 253 // surprisingly slow ... 254 aCacheUpperName = ScGlobal::pCharClass->uppercase(rName); 255 } 256 aUpperName = aCacheUpperName; 257 258 for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++) 259 if (maTabs[i]) 260 { 261 if (aUpperName == maTabs[i]->GetUpperName()) 262 { 263 rTab = i; 264 return true; 265 } 266 } 267 rTab = 0; 268 return false; 269 } 270 271 std::vector<OUString> ScDocument::GetAllTableNames() const 272 { 273 std::vector<OUString> aNames; 274 aNames.reserve(maTabs.size()); 275 for (const auto& a : maTabs) 276 { 277 // Positions need to be preserved for ScCompiler and address convention 278 // context, so still push an empty string for NULL tabs. 279 OUString aName; 280 if (a) 281 { 282 const ScTable& rTab = *a; 283 aName = rTab.GetName(); 284 } 285 aNames.push_back(aName); 286 } 287 288 return aNames; 289 } 290 291 ScDBData* ScDocument::GetAnonymousDBData(SCTAB nTab) 292 { 293 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 294 return maTabs[nTab]->GetAnonymousDBData(); 295 return nullptr; 296 } 297 298 SCTAB ScDocument::GetTableCount() const 299 { 300 return static_cast<SCTAB>(maTabs.size()); 301 } 302 303 void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData) 304 { 305 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 306 maTabs[nTab]->SetAnonymousDBData(std::move(pDBData)); 307 } 308 309 void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData ) 310 { 311 mpAnonymousDBData = std::move(pDBData); 312 } 313 314 ScDBData* ScDocument::GetAnonymousDBData() 315 { 316 return mpAnonymousDBData.get(); 317 } 318 319 bool ScDocument::ValidTabName( const OUString& rName ) 320 { 321 if (rName.isEmpty()) 322 return false; 323 sal_Int32 nLen = rName.getLength(); 324 325 #if 1 326 // Restrict sheet names to what Excel accepts. 327 /* TODO: We may want to remove this restriction for full ODFF compliance. 328 * Merely loading and calculating ODF documents using these characters in 329 * sheet names is not affected by this, but all sheet name editing and 330 * copying functionality is, maybe falling back to "Sheet4" or similar. */ 331 for (sal_Int32 i = 0; i < nLen; ++i) 332 { 333 const sal_Unicode c = rName[i]; 334 switch (c) 335 { 336 case ':': 337 case '\\': 338 case '/': 339 case '?': 340 case '*': 341 case '[': 342 case ']': 343 // these characters are not allowed to match XL's convention. 344 return false; 345 case '\'': 346 if (i == 0 || i == nLen - 1) 347 // single quote is not allowed at the first or last 348 // character position. 349 return false; 350 break; 351 } 352 } 353 #endif 354 355 return true; 356 } 357 358 bool ScDocument::ValidNewTabName( const OUString& rName ) const 359 { 360 bool bValid = ValidTabName(rName); 361 if (!bValid) 362 return false; 363 OUString aUpperName = ScGlobal::pCharClass->uppercase(rName); 364 for (const auto& a : maTabs) 365 { 366 if (!a) 367 continue; 368 const OUString& rOldName = a->GetUpperName(); 369 bValid = rOldName != aUpperName; 370 if (!bValid) 371 break; 372 } 373 return bValid; 374 } 375 376 void ScDocument::CreateValidTabName(OUString& rName) const 377 { 378 if ( !ValidTabName(rName) ) 379 { 380 // Find new one 381 382 // Get Custom prefix 383 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions(); 384 const OUString& aStrTable = rOpt.GetInitTabPrefix(); 385 386 bool bOk = false; 387 388 // First test if the prefix is valid, if so only avoid doubles 389 bool bPrefix = ValidTabName( aStrTable ); 390 OSL_ENSURE(bPrefix, "Invalid Table Name"); 391 SCTAB nDummy; 392 393 for ( SCTAB i = static_cast<SCTAB>(maTabs.size())+1; !bOk ; i++ ) 394 { 395 OUStringBuffer aBuf; 396 aBuf.append(aStrTable); 397 aBuf.append(static_cast<sal_Int32>(i)); 398 rName = aBuf.makeStringAndClear(); 399 if (bPrefix) 400 bOk = ValidNewTabName( rName ); 401 else 402 bOk = !GetTable( rName, nDummy ); 403 } 404 } 405 else 406 { 407 // testing the supplied Name 408 409 if ( !ValidNewTabName(rName) ) 410 { 411 SCTAB i = 1; 412 OUStringBuffer aName; 413 do 414 { 415 i++; 416 aName = rName; 417 aName.append('_'); 418 aName.append(static_cast<sal_Int32>(i)); 419 } 420 while (!ValidNewTabName(aName.toString()) && (i < MAXTAB+1)); 421 rName = aName.makeStringAndClear(); 422 } 423 } 424 } 425 426 void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const 427 { 428 aNames.clear();//ensure that the vector is empty 429 430 // Get Custom prefix 431 const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions(); 432 const OUString& aStrTable = rOpt.GetInitTabPrefix(); 433 434 OUStringBuffer rName; 435 436 // First test if the prefix is valid, if so only avoid doubles 437 bool bPrefix = ValidTabName( aStrTable ); 438 OSL_ENSURE(bPrefix, "Invalid Table Name"); 439 SCTAB nDummy; 440 SCTAB i = static_cast<SCTAB>(maTabs.size())+1; 441 442 for (SCTAB j = 0; j < nCount; ++j) 443 { 444 bool bOk = false; 445 while(!bOk) 446 { 447 rName = aStrTable; 448 rName.append(static_cast<sal_Int32>(i)); 449 if (bPrefix) 450 bOk = ValidNewTabName( rName.toString() ); 451 else 452 bOk = !GetTable( rName.toString(), nDummy ); 453 i++; 454 } 455 aNames.push_back(rName.makeStringAndClear()); 456 } 457 } 458 459 void ScDocument::AppendTabOnLoad(const OUString& rName) 460 { 461 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size()); 462 if (!ValidTab(nTabCount)) 463 // max table count reached. No more tables. 464 return; 465 466 OUString aName = rName; 467 CreateValidTabName(aName); 468 maTabs.emplace_back( new ScTable(this, nTabCount, aName) ); 469 } 470 471 void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName) 472 { 473 if (!ValidTab(nTab) || static_cast<SCTAB>(maTabs.size()) <= nTab) 474 return; 475 476 if (!ValidTabName(rName)) 477 return; 478 479 maTabs[nTab]->SetName(rName); 480 } 481 482 void ScDocument::InvalidateStreamOnSave() 483 { 484 for (const auto& a : maTabs) 485 { 486 if (a) 487 a->SetStreamValid(false); 488 } 489 } 490 491 bool ScDocument::InsertTab( 492 SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab ) 493 { 494 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size()); 495 bool bValid = ValidTab(nTabCount); 496 if ( !bExternalDocument ) // else test rName == "'Doc'!Tab" first 497 bValid = (bValid && ValidNewTabName(rName)); 498 if (bValid) 499 { 500 if (nPos == SC_TAB_APPEND || nPos >= nTabCount) 501 { 502 nPos = maTabs.size(); 503 maTabs.emplace_back( new ScTable(this, nTabCount, rName) ); 504 if ( bExternalDocument ) 505 maTabs[nTabCount]->SetVisible( false ); 506 } 507 else 508 { 509 if (ValidTab(nPos) && (nPos < nTabCount)) 510 { 511 sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1); 512 513 ScRange aRange( 0,0,nPos, MAXCOL,MAXROW,MAXTAB ); 514 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); 515 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); 516 if (pRangeName) 517 pRangeName->UpdateInsertTab(aCxt); 518 pDBCollection->UpdateReference( 519 URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); 520 if (pDPCollection) 521 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); 522 if (pDetOpList) 523 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 ); 524 UpdateChartRef( URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); 525 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 ); 526 if ( pUnoBroadcaster ) 527 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) ); 528 529 for (const auto& a : maTabs) 530 { 531 if (a) 532 a->UpdateInsertTab(aCxt); 533 } 534 maTabs.emplace(maTabs.begin() + nPos, new ScTable(this, nPos, rName)); 535 536 // UpdateBroadcastAreas must be called between UpdateInsertTab, 537 // which ends listening, and StartAllListeners, to not modify 538 // areas that are to be inserted by starting listeners. 539 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1); 540 for (const auto& a : maTabs) 541 { 542 if (a) 543 a->UpdateCompile(); 544 } 545 546 StartAllListeners(); 547 548 if (pValidationList) 549 { 550 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 551 pValidationList->UpdateInsertTab(aCxt); 552 } 553 554 bValid = true; 555 } 556 else 557 bValid = false; 558 } 559 } 560 561 if (bValid) 562 { 563 sc::SetFormulaDirtyContext aCxt; 564 aCxt.mbClearTabDeletedFlag = bUndoDeleteTab; 565 aCxt.mnTabDeletedStart = nPos; 566 aCxt.mnTabDeletedEnd = nPos; 567 SetAllFormulasDirty(aCxt); 568 569 if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer()) 570 { 571 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 572 while (pViewShell) 573 { 574 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, ""); 575 pViewShell = SfxViewShell::GetNext(*pViewShell); 576 } 577 } 578 } 579 580 return bValid; 581 } 582 583 bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames, 584 bool bNamesValid ) 585 { 586 SCTAB nNewSheets = static_cast<SCTAB>(rNames.size()); 587 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size()); 588 bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets); 589 590 if (bValid) 591 { 592 if (nPos == SC_TAB_APPEND || nPos >= nTabCount) 593 { 594 for ( SCTAB i = 0; i < nNewSheets; ++i ) 595 { 596 maTabs.emplace_back( new ScTable(this, nTabCount + i, rNames.at(i)) ); 597 } 598 } 599 else 600 { 601 if (ValidTab(nPos) && (nPos < nTabCount)) 602 { 603 sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets); 604 ScRange aRange( 0,0,nPos, MAXCOL,MAXROW,MAXTAB ); 605 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets ); 606 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets ); 607 if (pRangeName) 608 pRangeName->UpdateInsertTab(aCxt); 609 pDBCollection->UpdateReference( 610 URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,nNewSheets ); 611 if (pDPCollection) 612 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets ); 613 if (pDetOpList) 614 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets ); 615 UpdateChartRef( URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,nNewSheets ); 616 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets ); 617 if ( pUnoBroadcaster ) 618 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) ); 619 620 for (const auto& a : maTabs) 621 { 622 if (a) 623 a->UpdateInsertTab(aCxt); 624 } 625 for (SCTAB i = 0; i < nNewSheets; ++i) 626 { 627 maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(this, nPos + i, rNames.at(i)) ); 628 } 629 630 // UpdateBroadcastAreas must be called between UpdateInsertTab, 631 // which ends listening, and StartAllListeners, to not modify 632 // areas that are to be inserted by starting listeners. 633 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets); 634 for (const auto& a : maTabs) 635 { 636 if (a) 637 a->UpdateCompile(); 638 } 639 640 StartAllListeners(); 641 642 if (pValidationList) 643 { 644 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 645 pValidationList->UpdateInsertTab(aCxt); 646 } 647 648 bValid = true; 649 } 650 else 651 bValid = false; 652 } 653 } 654 655 if (bValid) 656 { 657 sc::SetFormulaDirtyContext aCxt; 658 SetAllFormulasDirty(aCxt); 659 } 660 661 return bValid; 662 } 663 664 bool ScDocument::DeleteTab( SCTAB nTab ) 665 { 666 bool bValid = false; 667 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 668 { 669 if (maTabs[nTab]) 670 { 671 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size()); 672 if (nTabCount > 1) 673 { 674 sc::AutoCalcSwitch aACSwitch(*this, false); 675 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1); 676 677 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab ); 678 DelBroadcastAreasInRange( aRange ); 679 680 // #i8180# remove database ranges etc. that are on the deleted tab 681 // (restored in undo with ScRefUndoData) 682 683 xColNameRanges->DeleteOnTab( nTab ); 684 xRowNameRanges->DeleteOnTab( nTab ); 685 pDBCollection->DeleteOnTab( nTab ); 686 if (pDPCollection) 687 pDPCollection->DeleteOnTab( nTab ); 688 if (pDetOpList) 689 pDetOpList->DeleteOnTab( nTab ); 690 DeleteAreaLinksOnTab( nTab ); 691 692 // normal reference update 693 694 aRange.aEnd.SetTab( static_cast<SCTAB>(maTabs.size())-1 ); 695 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 ); 696 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 ); 697 if (pRangeName) 698 pRangeName->UpdateDeleteTab(aCxt); 699 pDBCollection->UpdateReference( 700 URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 ); 701 if (pDPCollection) 702 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 ); 703 if (pDetOpList) 704 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 ); 705 UpdateChartRef( URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 ); 706 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 ); 707 if (pValidationList) 708 { 709 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 710 pValidationList->UpdateDeleteTab(aCxt); 711 } 712 if ( pUnoBroadcaster ) 713 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) ); 714 715 for (auto & pTab : maTabs) 716 if (pTab) 717 pTab->UpdateDeleteTab(aCxt); 718 719 maTabs.erase(maTabs.begin() + nTab); 720 // UpdateBroadcastAreas must be called between UpdateDeleteTab, 721 // which ends listening, and StartAllListeners, to not modify 722 // areas that are to be inserted by starting listeners. 723 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1); 724 for (const auto& a : maTabs) 725 { 726 if (a) 727 a->UpdateCompile(); 728 } 729 // Excel-Filter deletes some Tables while loading, Listeners will 730 // only be triggered after the loading is done. 731 if ( !bInsertingFromOtherDoc ) 732 { 733 StartAllListeners(); 734 735 sc::SetFormulaDirtyContext aFormulaDirtyCxt; 736 SetAllFormulasDirty(aFormulaDirtyCxt); 737 } 738 739 if (comphelper::LibreOfficeKit::isActive()) 740 { 741 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 742 while (pViewShell) 743 { 744 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, ""); 745 pViewShell = SfxViewShell::GetNext(*pViewShell); 746 } 747 } 748 749 bValid = true; 750 } 751 } 752 } 753 return bValid; 754 } 755 756 bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets ) 757 { 758 bool bValid = false; 759 if (ValidTab(nTab) && (nTab + nSheets) <= static_cast<SCTAB>(maTabs.size())) 760 { 761 if (maTabs[nTab]) 762 { 763 SCTAB nTabCount = static_cast<SCTAB>(maTabs.size()); 764 if (nTabCount > nSheets) 765 { 766 sc::AutoCalcSwitch aACSwitch(*this, false); 767 sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets); 768 769 for (SCTAB aTab = 0; aTab < nSheets; ++aTab) 770 { 771 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab + aTab ); 772 DelBroadcastAreasInRange( aRange ); 773 774 // #i8180# remove database ranges etc. that are on the deleted tab 775 // (restored in undo with ScRefUndoData) 776 777 xColNameRanges->DeleteOnTab( nTab + aTab ); 778 xRowNameRanges->DeleteOnTab( nTab + aTab ); 779 pDBCollection->DeleteOnTab( nTab + aTab ); 780 if (pDPCollection) 781 pDPCollection->DeleteOnTab( nTab + aTab ); 782 if (pDetOpList) 783 pDetOpList->DeleteOnTab( nTab + aTab ); 784 DeleteAreaLinksOnTab( nTab + aTab ); 785 } 786 787 if (pRangeName) 788 pRangeName->UpdateDeleteTab(aCxt); 789 790 // normal reference update 791 792 ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTabCount - 1 ); 793 xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets ); 794 xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets ); 795 pDBCollection->UpdateReference( 796 URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1*nSheets ); 797 if (pDPCollection) 798 pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets ); 799 if (pDetOpList) 800 pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets ); 801 UpdateChartRef( URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1*nSheets ); 802 UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets ); 803 if (pValidationList) 804 { 805 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 806 pValidationList->UpdateDeleteTab(aCxt); 807 } 808 if ( pUnoBroadcaster ) 809 pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) ); 810 811 for (auto & pTab : maTabs) 812 if (pTab) 813 pTab->UpdateDeleteTab(aCxt); 814 815 maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets); 816 // UpdateBroadcastAreas must be called between UpdateDeleteTab, 817 // which ends listening, and StartAllListeners, to not modify 818 // areas that are to be inserted by starting listeners. 819 UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets); 820 for (const auto& a : maTabs) 821 { 822 if (a) 823 a->UpdateCompile(); 824 } 825 // Excel-Filter deletes some Tables while loading, Listeners will 826 // only be triggered after the loading is done. 827 if ( !bInsertingFromOtherDoc ) 828 { 829 StartAllListeners(); 830 831 sc::SetFormulaDirtyContext aFormulaDirtyCxt; 832 SetAllFormulasDirty(aFormulaDirtyCxt); 833 } 834 835 if (comphelper::LibreOfficeKit::isActive()) 836 { 837 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 838 while (pViewShell) 839 { 840 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, ""); 841 pViewShell = SfxViewShell::GetNext(*pViewShell); 842 } 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. 874 if ( pChartListenerCollection ) 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 886 if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer()) 887 { 888 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 889 while (pViewShell) 890 { 891 pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_DOCUMENT_SIZE_CHANGED, ""); 892 pViewShell = SfxViewShell::GetNext(*pViewShell); 893 } 894 } 895 } 896 } 897 } 898 return bValid; 899 } 900 901 void ScDocument::SetVisible( SCTAB nTab, bool bVisible ) 902 { 903 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size())) 904 if (maTabs[nTab]) 905 maTabs[nTab]->SetVisible(bVisible); 906 } 907 908 bool ScDocument::IsVisible( SCTAB nTab ) const 909 { 910 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size())) 911 if (maTabs[nTab]) 912 return maTabs[nTab]->IsVisible(); 913 914 return false; 915 } 916 917 bool ScDocument::IsStreamValid( SCTAB nTab ) const 918 { 919 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 920 return maTabs[nTab]->IsStreamValid(); 921 922 return false; 923 } 924 925 void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock ) 926 { 927 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 928 maTabs[nTab]->SetStreamValid( bSet, bIgnoreLock ); 929 } 930 931 void ScDocument::LockStreamValid( bool bLock ) 932 { 933 mbStreamValidLocked = bLock; 934 } 935 936 bool ScDocument::IsPendingRowHeights( SCTAB nTab ) const 937 { 938 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 939 return maTabs[nTab]->IsPendingRowHeights(); 940 941 return false; 942 } 943 944 void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet ) 945 { 946 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 947 maTabs[nTab]->SetPendingRowHeights( bSet ); 948 } 949 950 void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL ) 951 { 952 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 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(); 966 967 // mirror existing objects: 968 969 if (mpDrawLayer) 970 { 971 SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); 972 OSL_ENSURE(pPage,"Page ?"); 973 if (pPage) 974 { 975 SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); 976 SdrObject* pObject = aIter.Next(); 977 while (pObject) 978 { 979 // objects with ScDrawObjData are re-positioned in SetPageSize, 980 // don't mirror again 981 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject ); 982 if ( !pData ) 983 mpDrawLayer->MirrorRTL( pObject ); 984 985 pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB ); 986 987 pObject = aIter.Next(); 988 } 989 } 990 } 991 } 992 } 993 994 bool ScDocument::IsLayoutRTL( SCTAB nTab ) const 995 { 996 if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] ) 997 return maTabs[nTab]->IsLayoutRTL(); 998 999 return false; 1000 } 1001 1002 bool ScDocument::IsNegativePage( SCTAB nTab ) const 1003 { 1004 // Negative page area is always used for RTL layout. 1005 // The separate method is used to find all RTL handling of drawing objects. 1006 return IsLayoutRTL( nTab ); 1007 } 1008 1009 /* ---------------------------------------------------------------------------- 1010 used search area: 1011 1012 GetCellArea - Only Data 1013 GetTableArea - Data / Attributes 1014 GetPrintArea - intended for character objects, 1015 sweeps attributes all the way to bottom / right 1016 ---------------------------------------------------------------------------- */ 1017 1018 bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const 1019 { 1020 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size())) 1021 if (maTabs[nTab]) 1022 return maTabs[nTab]->GetCellArea( rEndCol, rEndRow ); 1023 1024 rEndCol = 0; 1025 rEndRow = 0; 1026 return false; 1027 } 1028 1029 bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const 1030 { 1031 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size())) 1032 if (maTabs[nTab]) 1033 return maTabs[nTab]->GetTableArea( rEndCol, rEndRow ); 1034 1035 rEndCol = 0; 1036 rEndRow = 0; 1037 return false; 1038 } 1039 1040 bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const 1041 { 1042 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab]) 1043 return false; 1044 1045 SCCOL nCol1, nCol2; 1046 SCROW nRow1, nRow2; 1047 maTabs[nTab]->GetFirstDataPos(nCol1, nRow1); 1048 maTabs[nTab]->GetLastDataPos(nCol2, nRow2); 1049 1050 if (nCol1 > nCol2 || nRow1 > nRow2) 1051 // invalid range. 1052 return false; 1053 1054 // Make sure the area only shrinks, and doesn't grow. 1055 if (rStartCol < nCol1) 1056 rStartCol = nCol1; 1057 if (nCol2 < rEndCol) 1058 rEndCol = nCol2; 1059 if (rStartRow < nRow1) 1060 rStartRow = nRow1; 1061 if (nRow2 < rEndRow) 1062 rEndRow = nRow2; 1063 1064 if (rStartCol > rEndCol || rStartRow > rEndRow) 1065 // invalid range. 1066 return false; 1067 1068 return true; // success! 1069 } 1070 1071 bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol, 1072 SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly, 1073 bool bStickyTopRow, bool bStickyLeftCol, bool bConsiderCellNotes, 1074 bool bConsiderCellDrawObjects ) const 1075 { 1076 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab]) 1077 { 1078 o_bShrunk = false; 1079 return false; 1080 } 1081 return maTabs[nTab]->ShrinkToUsedDataArea( o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, 1082 bColumnsOnly, bStickyTopRow, bStickyLeftCol, bConsiderCellNotes, bConsiderCellDrawObjects ); 1083 } 1084 1085 SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const 1086 { 1087 const ScTable* pTab = FetchTable(nTab); 1088 if (!pTab) 1089 return -1; 1090 1091 return pTab->GetLastDataRow(nCol1, nCol2, nLastRow); 1092 } 1093 1094 // connected area 1095 1096 void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, 1097 SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const 1098 { 1099 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) 1100 maTabs[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown ); 1101 } 1102 1103 bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const 1104 { 1105 SCTAB nTab = rRange.aStart.Tab(); 1106 if (nTab != rRange.aEnd.Tab()) 1107 return true; 1108 1109 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) 1110 return maTabs[nTab]->GetDataAreaSubrange(rRange); 1111 1112 return true; 1113 } 1114 1115 void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, 1116 SCCOL& rEndCol, SCROW& rEndRow ) 1117 { 1118 if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size())) 1119 if (maTabs[nTab]) 1120 maTabs[nTab]->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow ); 1121 } 1122 1123 void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList ) 1124 { 1125 ScRangeListRef aNew = new ScRangeList; 1126 if (rRangeList.is()) 1127 { 1128 for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ ) 1129 { 1130 ScRange aRange( (*rRangeList)[i] ); 1131 if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL ) || 1132 ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MAXROW ) ) 1133 { 1134 SCCOL nStartCol = aRange.aStart.Col(); 1135 SCROW nStartRow = aRange.aStart.Row(); 1136 SCCOL nEndCol = aRange.aEnd.Col(); 1137 SCROW nEndRow = aRange.aEnd.Row(); 1138 SCTAB nTab = aRange.aStart.Tab(); 1139 if ( nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) 1140 maTabs[nTab]->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow); 1141 aRange.aStart.SetCol( nStartCol ); 1142 aRange.aStart.SetRow( nStartRow ); 1143 aRange.aEnd.SetCol( nEndCol ); 1144 aRange.aEnd.SetRow( nEndRow ); 1145 } 1146 aNew->push_back(aRange); 1147 } 1148 } 1149 else 1150 { 1151 OSL_FAIL("LimitChartIfAll: Ref==0"); 1152 } 1153 rRangeList = aNew; 1154 } 1155 1156 static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab ) 1157 { 1158 // without ScMarkData, leave start/end unchanged 1159 if ( pTabMark ) 1160 { 1161 for (SCTAB nTab=0; nTab< aMaxTab; ++nTab) 1162 if (pTabMark->GetTableSelect(nTab)) 1163 { 1164 // find first range of consecutive selected sheets 1165 rTabRangeStart = pTabMark->GetFirstSelected(); 1166 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) ) 1167 ++nTab; 1168 rTabRangeEnd = nTab; 1169 return; 1170 } 1171 } 1172 } 1173 1174 static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab ) 1175 { 1176 if ( pTabMark ) 1177 { 1178 // find next range of consecutive selected sheets after rTabRangeEnd 1179 for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab) 1180 if (pTabMark->GetTableSelect(nTab)) 1181 { 1182 rTabRangeStart = nTab; 1183 while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) ) 1184 ++nTab; 1185 rTabRangeEnd = nTab; 1186 return true; 1187 } 1188 } 1189 return false; 1190 } 1191 1192 bool ScDocument::CanInsertRow( const ScRange& rRange ) const 1193 { 1194 SCCOL nStartCol = rRange.aStart.Col(); 1195 SCROW nStartRow = rRange.aStart.Row(); 1196 SCTAB nStartTab = rRange.aStart.Tab(); 1197 SCCOL nEndCol = rRange.aEnd.Col(); 1198 SCROW nEndRow = rRange.aEnd.Row(); 1199 SCTAB nEndTab = rRange.aEnd.Tab(); 1200 PutInOrder( nStartCol, nEndCol ); 1201 PutInOrder( nStartRow, nEndRow ); 1202 PutInOrder( nStartTab, nEndTab ); 1203 SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1); 1204 1205 bool bTest = true; 1206 for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) 1207 if (maTabs[i]) 1208 bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize ); 1209 1210 return bTest; 1211 } 1212 1213 namespace { 1214 1215 struct SetDirtyIfPostponedHandler 1216 { 1217 void operator() (const ScTableUniquePtr & p) 1218 { 1219 if (p) 1220 p->SetDirtyIfPostponed(); 1221 } 1222 }; 1223 1224 struct BroadcastRecalcOnRefMoveHandler 1225 { 1226 void operator() (const ScTableUniquePtr & p) 1227 { 1228 if (p) 1229 p->BroadcastRecalcOnRefMove(); 1230 } 1231 1232 explicit BroadcastRecalcOnRefMoveHandler( ScDocument* pDoc ) : 1233 aSwitch( *pDoc, false), 1234 aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged) 1235 { 1236 } 1237 1238 private: 1239 sc::AutoCalcSwitch const aSwitch; // first for ctor/dtor order, destroy second 1240 ScBulkBroadcast const aBulk; // second for ctor/dtor order, destroy first 1241 }; 1242 1243 } 1244 1245 bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab, 1246 SCCOL nEndCol, SCTAB nEndTab, 1247 SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc, 1248 const ScMarkData* pTabMark ) 1249 { 1250 SCTAB i; 1251 1252 PutInOrder( nStartCol, nEndCol ); 1253 PutInOrder( nStartTab, nEndTab ); 1254 if ( pTabMark ) 1255 { 1256 nStartTab = 0; 1257 nEndTab = static_cast<SCTAB>(maTabs.size()) -1; 1258 } 1259 1260 bool bTest = true; 1261 bool bRet = false; 1262 bool bOldAutoCalc = GetAutoCalc(); 1263 SetAutoCalc( false ); // avoid multiple calculations 1264 for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) 1265 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1266 bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize); 1267 if (bTest) 1268 { 1269 // UpdateBroadcastAreas have to be called before UpdateReference, so that entries 1270 // aren't shifted that would be rebuild at UpdateReference 1271 1272 // handle chunks of consecutive selected sheets together 1273 SCTAB nTabRangeStart = nStartTab; 1274 SCTAB nTabRangeEnd = nEndTab; 1275 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1276 ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MAXROW, nTabRangeEnd); 1277 sc::EndListeningContext aEndListenCxt(*this); 1278 1279 std::vector<ScAddress> aGroupPos; 1280 do 1281 { 1282 aShiftedRange.aStart.SetTab(nTabRangeStart); 1283 aShiftedRange.aEnd.SetTab(nTabRangeEnd); 1284 1285 // Collect all formula groups that will get split by the shifting, 1286 // and end all their listening. Record the position of the top 1287 // cell of the topmost group, and the position of the bottom cell 1288 // of the bottommost group. 1289 EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos); 1290 1291 UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0); 1292 } 1293 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1294 1295 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1296 1297 sc::RefUpdateContext aCxt(*this); 1298 aCxt.meMode = URM_INSDEL; 1299 aCxt.maRange = aShiftedRange; 1300 aCxt.mnRowDelta = nSize; 1301 do 1302 { 1303 aCxt.maRange.aStart.SetTab(nTabRangeStart); 1304 aCxt.maRange.aEnd.SetTab(nTabRangeEnd); 1305 UpdateReference(aCxt, pRefUndoDoc, false); // without drawing objects 1306 } 1307 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1308 1309 // UpdateReference should have set "needs listening" flags to those 1310 // whose references have been modified. We also need to set this flag 1311 // to those that were in the groups that got split by shifting. 1312 SetNeedsListeningGroups(aGroupPos); 1313 1314 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++) 1315 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1316 maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize ); 1317 1318 // UpdateRef for drawing layer must be after inserting, 1319 // when the new row heights are known. 1320 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++) 1321 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1322 maTabs[i]->UpdateDrawRef( URM_INSDEL, 1323 nStartCol, nStartRow, nStartTab, nEndCol, MAXROW, nEndTab, 1324 0, static_cast<SCROW>(nSize), 0 ); 1325 1326 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) 1327 { // A new Listening is needed when references to deleted ranges are restored, 1328 // previous Listeners were removed in FormulaCell UpdateReference. 1329 StartAllListeners(); 1330 } 1331 else 1332 { // Listeners have been removed in UpdateReference 1333 StartNeededListeners(); 1334 1335 // At least all cells using range names pointing relative to the 1336 // moved range must be recalculated, and all cells marked postponed 1337 // dirty. 1338 for (const auto& a : maTabs) 1339 { 1340 if (a) 1341 a->SetDirtyIfPostponed(); 1342 } 1343 1344 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this)); 1345 } 1346 bRet = true; 1347 } 1348 SetAutoCalc( bOldAutoCalc ); 1349 if ( bRet && pChartListenerCollection ) 1350 pChartListenerCollection->UpdateDirtyCharts(); 1351 return bRet; 1352 } 1353 1354 bool ScDocument::InsertRow( const ScRange& rRange ) 1355 { 1356 return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(), 1357 rRange.aEnd.Col(), rRange.aEnd.Tab(), 1358 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) ); 1359 } 1360 1361 void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab, 1362 SCCOL nEndCol, SCTAB nEndTab, 1363 SCROW nStartRow, SCSIZE nSize, 1364 ScDocument* pRefUndoDoc, bool* pUndoOutline, 1365 const ScMarkData* pTabMark ) 1366 { 1367 SCTAB i; 1368 1369 PutInOrder( nStartCol, nEndCol ); 1370 PutInOrder( nStartTab, nEndTab ); 1371 if ( pTabMark ) 1372 { 1373 nStartTab = 0; 1374 nEndTab = static_cast<SCTAB>(maTabs.size())-1; 1375 } 1376 1377 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations 1378 1379 // handle chunks of consecutive selected sheets together 1380 SCTAB nTabRangeStart = nStartTab; 1381 SCTAB nTabRangeEnd = nEndTab; 1382 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1383 do 1384 { 1385 if ( ValidRow(nStartRow+nSize) ) 1386 { 1387 DelBroadcastAreasInRange( ScRange( 1388 ScAddress( nStartCol, nStartRow, nTabRangeStart ), 1389 ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) ); 1390 UpdateBroadcastAreas( URM_INSDEL, ScRange( 1391 ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ), 1392 ScAddress( nEndCol, MAXROW, nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 ); 1393 } 1394 else 1395 DelBroadcastAreasInRange( ScRange( 1396 ScAddress( nStartCol, nStartRow, nTabRangeStart ), 1397 ScAddress( nEndCol, MAXROW, nTabRangeEnd ) ) ); 1398 } 1399 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1400 1401 sc::RefUpdateContext aCxt(*this); 1402 const bool bLastRowIncluded = (nStartRow + nSize == MAXROWCOUNT && ValidRow(nStartRow)); 1403 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded ) 1404 { 1405 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1406 aCxt.meMode = URM_INSDEL; 1407 aCxt.mnRowDelta = -static_cast<SCROW>(nSize); 1408 if (bLastRowIncluded) 1409 { 1410 // Last row is included, shift a virtually non-existent row in. 1411 aCxt.maRange = ScRange( nStartCol, MAXROWCOUNT, nTabRangeStart, nEndCol, MAXROWCOUNT, nTabRangeEnd); 1412 } 1413 else 1414 { 1415 aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MAXROW, nTabRangeEnd); 1416 } 1417 do 1418 { 1419 UpdateReference(aCxt, pRefUndoDoc, true, false); 1420 } 1421 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1422 } 1423 1424 if (pUndoOutline) 1425 *pUndoOutline = false; 1426 1427 // Keep track of the positions of all formula groups that have been joined 1428 // during row deletion. 1429 std::vector<ScAddress> aGroupPos; 1430 1431 for ( i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++) 1432 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1433 maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos); 1434 1435 // Newly joined groups have some of their members still listening. We 1436 // need to make sure none of them are listening. 1437 EndListeningGroups(aGroupPos); 1438 1439 // Mark all joined groups for group listening. 1440 SetNeedsListeningGroups(aGroupPos); 1441 1442 if ( ValidRow(nStartRow+nSize) || bLastRowIncluded ) 1443 { 1444 // Listeners have been removed in UpdateReference 1445 StartNeededListeners(); 1446 1447 // At least all cells using range names pointing relative to the moved 1448 // range must be recalculated, and all cells marked postponed dirty. 1449 for (const auto& a : maTabs) 1450 { 1451 if (a) 1452 a->SetDirtyIfPostponed(); 1453 } 1454 1455 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this)); 1456 } 1457 1458 if (pChartListenerCollection) 1459 pChartListenerCollection->UpdateDirtyCharts(); 1460 } 1461 1462 void ScDocument::DeleteRow( const ScRange& rRange ) 1463 { 1464 DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(), 1465 rRange.aEnd.Col(), rRange.aEnd.Tab(), 1466 rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) ); 1467 } 1468 1469 bool ScDocument::CanInsertCol( const ScRange& rRange ) const 1470 { 1471 SCCOL nStartCol = rRange.aStart.Col(); 1472 SCROW nStartRow = rRange.aStart.Row(); 1473 SCTAB nStartTab = rRange.aStart.Tab(); 1474 SCCOL nEndCol = rRange.aEnd.Col(); 1475 SCROW nEndRow = rRange.aEnd.Row(); 1476 SCTAB nEndTab = rRange.aEnd.Tab(); 1477 PutInOrder( nStartCol, nEndCol ); 1478 PutInOrder( nStartRow, nEndRow ); 1479 PutInOrder( nStartTab, nEndTab ); 1480 SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1); 1481 1482 bool bTest = true; 1483 for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) 1484 if (maTabs[i]) 1485 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize ); 1486 1487 return bTest; 1488 } 1489 1490 bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab, 1491 SCROW nEndRow, SCTAB nEndTab, 1492 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc, 1493 const ScMarkData* pTabMark ) 1494 { 1495 SCTAB i; 1496 1497 PutInOrder( nStartRow, nEndRow ); 1498 PutInOrder( nStartTab, nEndTab ); 1499 if ( pTabMark ) 1500 { 1501 nStartTab = 0; 1502 nEndTab = static_cast<SCTAB>(maTabs.size())-1; 1503 } 1504 1505 bool bTest = true; 1506 bool bRet = false; 1507 bool bOldAutoCalc = GetAutoCalc(); 1508 SetAutoCalc( false ); // avoid multiple calculations 1509 for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++) 1510 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1511 bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize ); 1512 if (bTest) 1513 { 1514 // handle chunks of consecutive selected sheets together 1515 SCTAB nTabRangeStart = nStartTab; 1516 SCTAB nTabRangeEnd = nEndTab; 1517 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1518 do 1519 { 1520 UpdateBroadcastAreas( URM_INSDEL, ScRange( 1521 ScAddress( nStartCol, nStartRow, nTabRangeStart ), 1522 ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 ); 1523 } 1524 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1525 1526 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1527 1528 sc::RefUpdateContext aCxt(*this); 1529 aCxt.meMode = URM_INSDEL; 1530 aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MAXCOL, nEndRow, nTabRangeEnd); 1531 aCxt.mnColDelta = nSize; 1532 do 1533 { 1534 UpdateReference(aCxt, pRefUndoDoc, true, false); 1535 } 1536 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1537 1538 for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++) 1539 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1540 maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize); 1541 1542 if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) 1543 { // A new Listening is needed when references to deleted ranges are restored, 1544 // previous Listeners were removed in FormulaCell UpdateReference. 1545 StartAllListeners(); 1546 } 1547 else 1548 { 1549 // Listeners have been removed in UpdateReference 1550 StartNeededListeners(); 1551 // At least all cells using range names pointing relative to the 1552 // moved range must be recalculated, and all cells marked postponed 1553 // dirty. 1554 std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler()); 1555 // Cells containing functions such as CELL, COLUMN or ROW may have 1556 // changed their values on relocation. Broadcast them. 1557 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this)); 1558 } 1559 bRet = true; 1560 } 1561 SetAutoCalc( bOldAutoCalc ); 1562 if ( bRet && pChartListenerCollection ) 1563 pChartListenerCollection->UpdateDirtyCharts(); 1564 return bRet; 1565 } 1566 1567 bool ScDocument::InsertCol( const ScRange& rRange ) 1568 { 1569 return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(), 1570 rRange.aEnd.Row(), rRange.aEnd.Tab(), 1571 rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) ); 1572 } 1573 1574 void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab, 1575 SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc, 1576 bool* pUndoOutline, const ScMarkData* pTabMark ) 1577 { 1578 SCTAB i; 1579 1580 PutInOrder( nStartRow, nEndRow ); 1581 PutInOrder( nStartTab, nEndTab ); 1582 if ( pTabMark ) 1583 { 1584 nStartTab = 0; 1585 nEndTab = static_cast<SCTAB>(maTabs.size())-1; 1586 } 1587 1588 sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations 1589 1590 // handle chunks of consecutive selected sheets together 1591 SCTAB nTabRangeStart = nStartTab; 1592 SCTAB nTabRangeEnd = nEndTab; 1593 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1594 do 1595 { 1596 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) ) 1597 { 1598 DelBroadcastAreasInRange( ScRange( 1599 ScAddress( nStartCol, nStartRow, nTabRangeStart ), 1600 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) ); 1601 UpdateBroadcastAreas( URM_INSDEL, ScRange( 1602 ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ), 1603 ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 ); 1604 } 1605 else 1606 DelBroadcastAreasInRange( ScRange( 1607 ScAddress( nStartCol, nStartRow, nTabRangeStart ), 1608 ScAddress( MAXCOL, nEndRow, nTabRangeEnd ) ) ); 1609 } 1610 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1611 1612 sc::RefUpdateContext aCxt(*this); 1613 const bool bLastColIncluded = (nStartCol + nSize == MAXCOLCOUNT && ValidCol(nStartCol)); 1614 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded ) 1615 { 1616 lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ); 1617 aCxt.meMode = URM_INSDEL; 1618 aCxt.mnColDelta = -static_cast<SCCOL>(nSize); 1619 if (bLastColIncluded) 1620 { 1621 // Last column is included, shift a virtually non-existent column in. 1622 aCxt.maRange = ScRange( MAXCOLCOUNT, nStartRow, nTabRangeStart, MAXCOLCOUNT, nEndRow, nTabRangeEnd); 1623 } 1624 else 1625 { 1626 aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart, 1627 MAXCOL, nEndRow, nTabRangeEnd); 1628 } 1629 do 1630 { 1631 UpdateReference(aCxt, pRefUndoDoc, true, false); 1632 } 1633 while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) ); 1634 } 1635 1636 if (pUndoOutline) 1637 *pUndoOutline = false; 1638 1639 for (i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); ++i) 1640 { 1641 if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i))) 1642 maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline); 1643 } 1644 1645 if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded ) 1646 { 1647 // Listeners have been removed in UpdateReference 1648 StartNeededListeners(); 1649 1650 // At least all cells using range names pointing relative to the moved 1651 // range must be recalculated, and all cells marked postponed dirty. 1652 for (const auto& a : maTabs) 1653 { 1654 if (a) 1655 a->SetDirtyIfPostponed(); 1656 } 1657 1658 std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler( this)); 1659 } 1660 1661 if (pChartListenerCollection) 1662 pChartListenerCollection->UpdateDirtyCharts(); 1663 } 1664 1665 void ScDocument::DeleteCol( const ScRange& rRange ) 1666 { 1667 DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(), 1668 rRange.aEnd.Row(), rRange.aEnd.Tab(), 1669 rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) ); 1670 } 1671 1672 // for Area-Links: Insert/delete cells, when the range is changed. 1673 // (without Paint) 1674 1675 static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew, 1676 ScRange& rColRange, bool& rInsCol, bool& rDelCol, 1677 ScRange& rRowRange, bool& rInsRow, bool& rDelRow ) 1678 { 1679 OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" ); 1680 1681 rInsCol = rDelCol = rInsRow = rDelRow = false; 1682 1683 SCCOL nStartX = rOld.aStart.Col(); 1684 SCROW nStartY = rOld.aStart.Row(); 1685 SCCOL nOldEndX = rOld.aEnd.Col(); 1686 SCROW nOldEndY = rOld.aEnd.Row(); 1687 SCCOL nNewEndX = rNew.aEnd.Col(); 1688 SCROW nNewEndY = rNew.aEnd.Row(); 1689 SCTAB nTab = rOld.aStart.Tab(); 1690 1691 // if more rows, columns are inserted/deleted at the old height. 1692 bool bGrowY = ( nNewEndY > nOldEndY ); 1693 SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY; 1694 SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX; 1695 1696 // Columns 1697 1698 if ( nNewEndX > nOldEndX ) // Insert columns 1699 { 1700 rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab ); 1701 rInsCol = true; 1702 } 1703 else if ( nNewEndX < nOldEndX ) // Delete columns 1704 { 1705 rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab ); 1706 rDelCol = true; 1707 } 1708 1709 // Rows 1710 1711 if ( nNewEndY > nOldEndY ) // Insert rows 1712 { 1713 rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab ); 1714 rInsRow = true; 1715 } 1716 else if ( nNewEndY < nOldEndY ) // Delete rows 1717 { 1718 rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab ); 1719 rDelRow = true; 1720 } 1721 } 1722 1723 bool ScDocument::HasPartOfMerged( const ScRange& rRange ) 1724 { 1725 bool bPart = false; 1726 SCTAB nTab = rRange.aStart.Tab(); 1727 1728 SCCOL nStartX = rRange.aStart.Col(); 1729 SCROW nStartY = rRange.aStart.Row(); 1730 SCCOL nEndX = rRange.aEnd.Col(); 1731 SCROW nEndY = rRange.aEnd.Row(); 1732 1733 if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab, 1734 HasAttrFlags::Merged | HasAttrFlags::Overlapped )) 1735 { 1736 ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab ); 1737 ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab ); 1738 1739 bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() || 1740 nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() ); 1741 } 1742 return bPart; 1743 } 1744 1745 size_t ScDocument::GetFormulaHash( const ScAddress& rPos ) const 1746 { 1747 SCTAB nTab = rPos.Tab(); 1748 if (!ValidTab(nTab) || static_cast<size_t>(nTab) >= maTabs.size() || !maTabs[nTab]) 1749 return 0; 1750 1751 return maTabs[nTab]->GetFormulaHash(rPos.Col(), rPos.Row()); 1752 } 1753 1754 ScFormulaVectorState ScDocument::GetFormulaVectorState( const ScAddress& rPos ) const 1755 { 1756 SCTAB nTab = rPos.Tab(); 1757 if (!ValidTab(nTab) || static_cast<size_t>(nTab) >= maTabs.size() || !maTabs[nTab]) 1758 return FormulaVectorUnknown; 1759 1760 return maTabs[nTab]->GetFormulaVectorState(rPos.Col(), rPos.Row()); 1761 } 1762 1763 formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos ) 1764 { 1765 SCTAB nTab = rPos.Tab(); 1766 if (!TableExists(nTab)) 1767 return formula::FormulaTokenRef(); 1768 1769 return maTabs[nTab]->ResolveStaticReference(rPos.Col(), rPos.Row()); 1770 } 1771 1772 formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange ) 1773 { 1774 SCTAB nTab = rRange.aStart.Tab(); 1775 if (nTab != rRange.aEnd.Tab() || !TableExists(nTab)) 1776 return formula::FormulaTokenRef(); 1777 1778 return maTabs[nTab]->ResolveStaticReference( 1779 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 1780 } 1781 1782 formula::VectorRefArray ScDocument::FetchVectorRefArray( const ScAddress& rPos, SCROW nLength ) 1783 { 1784 SCTAB nTab = rPos.Tab(); 1785 if (!TableExists(nTab)) 1786 return formula::VectorRefArray(); 1787 1788 return maTabs[nTab]->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1); 1789 } 1790 1791 #ifdef DBG_UTIL 1792 void ScDocument::AssertNoInterpretNeeded( const ScAddress& rPos, SCROW nLength ) 1793 { 1794 SCTAB nTab = rPos.Tab(); 1795 assert(TableExists(nTab)); 1796 return maTabs[nTab]->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1); 1797 } 1798 #endif 1799 1800 void ScDocument::UnlockAdjustHeight() 1801 { 1802 assert(nAdjustHeightLock > 0); 1803 if(nAdjustHeightLock > 0) 1804 --nAdjustHeightLock; 1805 } 1806 1807 bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength, const ScFormulaCellGroupRef& mxGroup ) 1808 { 1809 SCTAB nTab = rPos.Tab(); 1810 if (!TableExists(nTab)) 1811 return false; 1812 1813 return maTabs[nTab]->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup); 1814 } 1815 1816 bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew ) 1817 { 1818 if ( rOld == rNew ) 1819 return true; 1820 1821 bool bOk = true; 1822 bool bInsCol,bDelCol,bInsRow,bDelRow; 1823 ScRange aColRange,aRowRange; 1824 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow ); 1825 1826 if ( bInsCol && !CanInsertCol( aColRange ) ) // Cells at the edge ? 1827 bOk = false; 1828 if ( bInsRow && !CanInsertRow( aRowRange ) ) // Cells at the edge ? 1829 bOk = false; 1830 1831 if ( bInsCol || bDelCol ) 1832 { 1833 aColRange.aEnd.SetCol(MAXCOL); 1834 if ( HasPartOfMerged(aColRange) ) 1835 bOk = false; 1836 } 1837 if ( bInsRow || bDelRow ) 1838 { 1839 aRowRange.aEnd.SetRow(MAXROW); 1840 if ( HasPartOfMerged(aRowRange) ) 1841 bOk = false; 1842 } 1843 1844 return bOk; 1845 } 1846 1847 void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear ) 1848 { 1849 if (bClear) 1850 DeleteAreaTab( rOld, InsertDeleteFlags::ALL ); 1851 1852 bool bInsCol,bDelCol,bInsRow,bDelRow; 1853 ScRange aColRange,aRowRange; 1854 lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow ); 1855 1856 if ( bInsCol ) 1857 InsertCol( aColRange ); // First insert columns 1858 if ( bInsRow ) 1859 InsertRow( aRowRange ); 1860 1861 if ( bDelRow ) 1862 DeleteRow( aRowRange ); // First delete rows 1863 if ( bDelCol ) 1864 DeleteCol( aColRange ); 1865 1866 // Expand references to inserted rows 1867 1868 if ( bInsCol || bInsRow ) 1869 { 1870 ScRange aGrowSource = rOld; 1871 aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() )); 1872 aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() )); 1873 SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0; 1874 SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0; 1875 UpdateGrow( aGrowSource, nGrowX, nGrowY ); 1876 } 1877 } 1878 1879 void ScDocument::DeleteArea( 1880 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark, 1881 InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans ) 1882 { 1883 sc::AutoCalcSwitch aACSwitch(*this, false); 1884 1885 PutInOrder( nCol1, nCol2 ); 1886 PutInOrder( nRow1, nRow2 ); 1887 1888 std::vector<ScAddress> aGroupPos; 1889 // Destroy and reconstruct listeners only if content is affected. 1890 bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag); 1891 if (bDelContent) 1892 { 1893 // Record the positions of top and/or bottom formula groups that intersect 1894 // the area borders. 1895 sc::EndListeningContext aCxt(*this); 1896 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0); 1897 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++) 1898 { 1899 if (rMark.GetTableSelect(i)) 1900 { 1901 aRange.aStart.SetTab(i); 1902 aRange.aEnd.SetTab(i); 1903 1904 EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos); 1905 } 1906 } 1907 aCxt.purgeEmptyBroadcasters(); 1908 } 1909 1910 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++) 1911 if (maTabs[i]) 1912 if ( rMark.GetTableSelect(i) || bIsUndo ) 1913 maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans); 1914 1915 if (bDelContent) 1916 { 1917 // Re-start listeners on those top bottom groups that have been split. 1918 SetNeedsListeningGroups(aGroupPos); 1919 StartNeededListeners(); 1920 1921 // If formula groups were split their listeners were destroyed and may 1922 // need to be notified now that they're restored, ScTable::DeleteArea() 1923 // couldn't do that. 1924 if (!aGroupPos.empty()) 1925 { 1926 ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0); 1927 for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++) 1928 { 1929 if (rMark.GetTableSelect(i)) 1930 { 1931 aRange.aStart.SetTab(i); 1932 aRange.aEnd.SetTab(i); 1933 SetDirty( aRange, true); 1934 } 1935 } 1936 } 1937 } 1938 } 1939 1940 void ScDocument::DeleteAreaTab(SCCOL nCol1, SCROW nRow1, 1941 SCCOL nCol2, SCROW nRow2, 1942 SCTAB nTab, InsertDeleteFlags nDelFlag) 1943 { 1944 PutInOrder( nCol1, nCol2 ); 1945 PutInOrder( nRow1, nRow2 ); 1946 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 1947 { 1948 bool bOldAutoCalc = GetAutoCalc(); 1949 SetAutoCalc( false ); // avoid multiple calculations 1950 maTabs[nTab]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag); 1951 SetAutoCalc( bOldAutoCalc ); 1952 } 1953 } 1954 1955 void ScDocument::DeleteAreaTab( const ScRange& rRange, InsertDeleteFlags nDelFlag ) 1956 { 1957 for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ ) 1958 DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(), 1959 rRange.aEnd.Col(), rRange.aEnd.Row(), 1960 nTab, nDelFlag ); 1961 } 1962 1963 void ScDocument::InitUndoSelected( const ScDocument* pSrcDoc, const ScMarkData& rTabSelection, 1964 bool bColInfo, bool bRowInfo ) 1965 { 1966 if (bIsUndo) 1967 { 1968 Clear(); 1969 1970 SharePooledResources(pSrcDoc); 1971 1972 for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++) 1973 if ( rTabSelection.GetTableSelect( nTab ) ) 1974 { 1975 ScTableUniquePtr pTable(new ScTable(this, nTab, OUString(), bColInfo, bRowInfo)); 1976 if (nTab < static_cast<SCTAB>(maTabs.size())) 1977 maTabs[nTab] = std::move(pTable); 1978 else 1979 maTabs.push_back(std::move(pTable)); 1980 } 1981 else 1982 { 1983 if (nTab < static_cast<SCTAB>(maTabs.size())) 1984 maTabs[nTab]=nullptr; 1985 else 1986 maTabs.push_back(nullptr); 1987 } 1988 } 1989 else 1990 { 1991 OSL_FAIL("InitUndo"); 1992 } 1993 } 1994 1995 void ScDocument::InitUndo( const ScDocument* pSrcDoc, SCTAB nTab1, SCTAB nTab2, 1996 bool bColInfo, bool bRowInfo ) 1997 { 1998 if (!bIsUndo) 1999 { 2000 OSL_FAIL("InitUndo"); 2001 return; 2002 } 2003 2004 Clear(); 2005 2006 // Undo document shares its pooled resources with the source document. 2007 SharePooledResources(pSrcDoc); 2008 2009 if (pSrcDoc->mpShell->GetMedium()) 2010 maFileURL = pSrcDoc->mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri); 2011 2012 if ( nTab2 >= static_cast<SCTAB>(maTabs.size())) 2013 maTabs.resize(nTab2 + 1); 2014 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++) 2015 { 2016 maTabs[nTab].reset(new ScTable(this, nTab, OUString(), bColInfo, bRowInfo)); 2017 } 2018 } 2019 2020 void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo ) 2021 { 2022 if (!bIsUndo) 2023 { 2024 OSL_FAIL("AddUndoTab"); 2025 return; 2026 } 2027 2028 if (nTab2 >= static_cast<SCTAB>(maTabs.size())) 2029 { 2030 maTabs.resize(nTab2+1); 2031 } 2032 2033 for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++) 2034 if (!maTabs[nTab]) 2035 { 2036 maTabs[nTab].reset( new ScTable(this, nTab, OUString(), bColInfo, bRowInfo) ); 2037 } 2038 } 2039 2040 void ScDocument::SetCutMode( bool bVal ) 2041 { 2042 if (bIsClip) 2043 GetClipParam().mbCutMode = bVal; 2044 else 2045 { 2046 OSL_FAIL("SetCutMode without bIsClip"); 2047 } 2048 } 2049 2050 bool ScDocument::IsCutMode() 2051 { 2052 if (bIsClip) 2053 return GetClipParam().mbCutMode; 2054 else 2055 { 2056 OSL_FAIL("IsCutMode without bIsClip"); 2057 return false; 2058 } 2059 } 2060 2061 void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, 2062 SCCOL nCol2, SCROW nRow2, SCTAB nTab2, 2063 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc, 2064 const ScMarkData* pMarks, bool bColRowFlags ) 2065 { 2066 PutInOrder( nCol1, nCol2 ); 2067 PutInOrder( nRow1, nRow2 ); 2068 PutInOrder( nTab1, nTab2 ); 2069 if (rDestDoc.aDocName.isEmpty()) 2070 rDestDoc.aDocName = aDocName; 2071 if (ValidTab(nTab1) && ValidTab(nTab2)) 2072 { 2073 sc::CopyToDocContext aCxt(rDestDoc); 2074 bool bOldAutoCalc = rDestDoc.GetAutoCalc(); 2075 rDestDoc.SetAutoCalc( false ); // avoid multiple calculations 2076 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size())); 2077 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++) 2078 { 2079 if (maTabs[i] && rDestDoc.maTabs[i]) 2080 maTabs[i]->CopyToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags, 2081 bOnlyMarked, rDestDoc.maTabs[i].get(), pMarks, 2082 false, bColRowFlags, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true ); 2083 } 2084 rDestDoc.SetAutoCalc(bOldAutoCalc); 2085 } 2086 } 2087 2088 void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, 2089 SCCOL nCol2, SCROW nRow2, SCTAB nTab2, 2090 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc) 2091 { 2092 PutInOrder( nCol1, nCol2 ); 2093 PutInOrder( nRow1, nRow2 ); 2094 PutInOrder( nTab1, nTab2 ); 2095 if (ValidTab(nTab1) && ValidTab(nTab2)) 2096 { 2097 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations 2098 2099 if (nTab1 > 0) 2100 CopyToDocument(0, 0, 0, MAXCOL, MAXROW, nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc); 2101 2102 sc::CopyToDocContext aCxt(rDestDoc); 2103 assert( nTab2 < static_cast<SCTAB>(maTabs.size()) && nTab2 < static_cast<SCTAB>(rDestDoc.maTabs.size())); 2104 for (SCTAB i = nTab1; i <= nTab2; i++) 2105 { 2106 if (maTabs[i] && rDestDoc.maTabs[i]) 2107 maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags, 2108 bOnlyMarked, rDestDoc.maTabs[i].get()); 2109 } 2110 2111 if (nTab2 < MAXTAB) 2112 CopyToDocument(0, 0, nTab2+1, MAXCOL, MAXROW, MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc); 2113 } 2114 } 2115 2116 void ScDocument::CopyToDocument(const ScRange& rRange, 2117 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc, 2118 const ScMarkData* pMarks, bool bColRowFlags) 2119 { 2120 ScRange aNewRange = rRange; 2121 aNewRange.PutInOrder(); 2122 2123 if (rDestDoc.aDocName.isEmpty()) 2124 rDestDoc.aDocName = aDocName; 2125 2126 sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations 2127 2128 // tdf#102364 - in some pathological cases CopyToDocument() replacing cells with new cells 2129 // can lead to repetitive splitting and rejoining of the same formula group, which can get 2130 // quadratically expensive with large groups. So do the grouping just once at the end. 2131 sc::DelayFormulaGroupingSwitch delayGrouping( rDestDoc, true ); 2132 2133 sc::CopyToDocContext aCxt(rDestDoc); 2134 aCxt.setStartListening(false); 2135 2136 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size())); 2137 for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++) 2138 { 2139 ScTable* pTab = FetchTable(i); 2140 ScTable* pDestTab = rDestDoc.FetchTable(i); 2141 if (!pTab || !pDestTab) 2142 continue; 2143 2144 pTab->CopyToTable( 2145 aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(), 2146 nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags, 2147 /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true); 2148 } 2149 2150 delayGrouping.reset(); // groups need to be updated before setting up listeners 2151 rDestDoc.StartAllListeners(aNewRange); 2152 } 2153 2154 void ScDocument::UndoToDocument(const ScRange& rRange, 2155 InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc) 2156 { 2157 sc::AutoCalcSwitch aAutoCalcSwitch(*this, false); 2158 2159 ScRange aNewRange = rRange; 2160 aNewRange.PutInOrder(); 2161 SCTAB nTab1 = aNewRange.aStart.Tab(); 2162 SCTAB nTab2 = aNewRange.aEnd.Tab(); 2163 2164 sc::CopyToDocContext aCxt(rDestDoc); 2165 if (nTab1 > 0) 2166 CopyToDocument(0, 0, 0, MAXCOL, MAXROW, nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc); 2167 2168 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size())); 2169 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++) 2170 { 2171 if (maTabs[i] && rDestDoc.maTabs[i]) 2172 maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), 2173 aNewRange.aEnd.Col(), aNewRange.aEnd.Row(), 2174 nFlags, bOnlyMarked, rDestDoc.maTabs[i].get()); 2175 } 2176 2177 if (nTab2 < static_cast<SCTAB>(maTabs.size())) 2178 CopyToDocument(0, 0 , nTab2+1, MAXCOL, MAXROW, maTabs.size(), InsertDeleteFlags::FORMULA, false, rDestDoc); 2179 } 2180 2181 void ScDocument::CopyToClip(const ScClipParam& rClipParam, 2182 ScDocument* pClipDoc, const ScMarkData* pMarks, 2183 bool bKeepScenarioFlags, bool bIncludeObjects ) 2184 { 2185 OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" ); 2186 2187 if (bIsClip) 2188 return; 2189 2190 if (!pClipDoc) 2191 { 2192 SAL_WARN("sc", "CopyToClip: no ClipDoc"); 2193 pClipDoc = ScModule::GetClipDoc(); 2194 } 2195 2196 if (mpShell->GetMedium()) 2197 { 2198 pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri); 2199 // for unsaved files use the title name and adjust during save of file 2200 if (pClipDoc->maFileURL.isEmpty()) 2201 pClipDoc->maFileURL = mpShell->GetName(); 2202 } 2203 else 2204 { 2205 pClipDoc->maFileURL = mpShell->GetName(); 2206 } 2207 2208 //init maTabNames 2209 for (const auto& rxTab : maTabs) 2210 { 2211 if( rxTab ) 2212 { 2213 OUString aTabName = rxTab->GetName(); 2214 pClipDoc->maTabNames.push_back(aTabName); 2215 } 2216 else 2217 pClipDoc->maTabNames.emplace_back(); 2218 } 2219 2220 pClipDoc->aDocName = aDocName; 2221 pClipDoc->SetClipParam(rClipParam); 2222 ScRange aClipRange = rClipParam.getWholeRange(); 2223 SCTAB nEndTab = static_cast<SCTAB>(maTabs.size()); 2224 2225 pClipDoc->ResetClip(this, pMarks); 2226 2227 sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags); 2228 CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks); 2229 2230 for (SCTAB i = 0; i < nEndTab; ++i) 2231 { 2232 if (!maTabs[i] || i >= static_cast<SCTAB>(pClipDoc->maTabs.size()) || !pClipDoc->maTabs[i]) 2233 continue; 2234 2235 if ( pMarks && !pMarks->GetTableSelect(i) ) 2236 continue; 2237 2238 maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get()); 2239 2240 if (mpDrawLayer && bIncludeObjects) 2241 { 2242 // also copy drawing objects 2243 tools::Rectangle aObjRect = GetMMRect( 2244 aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i); 2245 mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect); 2246 } 2247 } 2248 2249 // Make sure to mark overlapped cells. 2250 pClipDoc->ExtendMerge(aClipRange, true); 2251 } 2252 2253 void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument* pDestDoc) 2254 { 2255 if (!pDestDoc) 2256 return; 2257 2258 ScTable* pSrcTab = rSrcRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr; 2259 ScTable* pDestTab = nDestTab < static_cast<SCTAB>(pDestDoc->maTabs.size()) ? pDestDoc->maTabs[nDestTab].get() : nullptr; 2260 2261 if (!pSrcTab || !pDestTab) 2262 return; 2263 2264 pDestDoc->GetFormatTable()->MergeFormatter(*GetFormatTable()); 2265 SvNumberFormatterMergeMap aMap = pDestDoc->GetFormatTable()->ConvertMergeTableToMap(); 2266 2267 pSrcTab->CopyStaticToDocument( 2268 rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(), 2269 aMap, pDestTab); 2270 } 2271 2272 void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc ) 2273 { 2274 if (!TableExists(rSrcPos.Tab()) || !rDestDoc.TableExists(rDestPos.Tab())) 2275 return; 2276 2277 ScTable& rSrcTab = *maTabs[rSrcPos.Tab()]; 2278 ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()]; 2279 2280 rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab); 2281 } 2282 2283 void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1, 2284 SCCOL nCol2, SCROW nRow2, 2285 SCTAB nTab, ScDocument* pClipDoc) 2286 { 2287 if (!bIsClip) 2288 { 2289 if (!pClipDoc) 2290 { 2291 SAL_WARN("sc", "CopyTabToClip: no ClipDoc"); 2292 pClipDoc = ScModule::GetClipDoc(); 2293 } 2294 2295 if (mpShell->GetMedium()) 2296 { 2297 pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri); 2298 // for unsaved files use the title name and adjust during save of file 2299 if (pClipDoc->maFileURL.isEmpty()) 2300 pClipDoc->maFileURL = mpShell->GetName(); 2301 } 2302 else 2303 { 2304 pClipDoc->maFileURL = mpShell->GetName(); 2305 } 2306 2307 //init maTabNames 2308 for (const auto& rxTab : maTabs) 2309 { 2310 if( rxTab ) 2311 { 2312 OUString aTabName = rxTab->GetName(); 2313 pClipDoc->maTabNames.push_back(aTabName); 2314 } 2315 else 2316 pClipDoc->maTabNames.emplace_back(); 2317 } 2318 2319 PutInOrder( nCol1, nCol2 ); 2320 PutInOrder( nRow1, nRow2 ); 2321 2322 ScClipParam& rClipParam = pClipDoc->GetClipParam(); 2323 pClipDoc->aDocName = aDocName; 2324 rClipParam.maRanges.RemoveAll(); 2325 rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0)); 2326 pClipDoc->ResetClip( this, nTab ); 2327 2328 sc::CopyToClipContext aCxt(*pClipDoc, false); 2329 if (nTab < static_cast<SCTAB>(maTabs.size()) && nTab < static_cast<SCTAB>(pClipDoc->maTabs.size())) 2330 if (maTabs[nTab] && pClipDoc->maTabs[nTab]) 2331 maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get()); 2332 2333 pClipDoc->GetClipParam().mbCutMode = false; 2334 } 2335 } 2336 2337 void ScDocument::TransposeClip( ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink ) 2338 { 2339 OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip, 2340 "TransposeClip with wrong Document" ); 2341 2342 // initialize 2343 // -> pTransClip has to be deleted before the original document! 2344 2345 pTransClip->ResetClip(this, nullptr); // all 2346 2347 // Take over range 2348 2349 if (pRangeName) 2350 { 2351 pTransClip->GetRangeName()->clear(); 2352 for (const auto& rEntry : *pRangeName) 2353 { 2354 sal_uInt16 nIndex = rEntry.second->GetIndex(); 2355 ScRangeData* pData = new ScRangeData(*rEntry.second); 2356 if (pTransClip->pRangeName->insert(pData)) 2357 pData->SetIndex(nIndex); 2358 } 2359 } 2360 2361 // The data 2362 2363 ScRange aClipRange = GetClipParam().getWholeRange(); 2364 if ( ValidRow(aClipRange.aEnd.Row()-aClipRange.aStart.Row()) ) 2365 { 2366 for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++) 2367 if (maTabs[i]) 2368 { 2369 OSL_ENSURE( pTransClip->maTabs[i], "TransposeClip: Table not there" ); 2370 maTabs[i]->TransposeClip( aClipRange.aStart.Col(), aClipRange.aStart.Row(), 2371 aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), 2372 pTransClip->maTabs[i].get(), nFlags, bAsLink ); 2373 2374 if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) ) 2375 { 2376 // Drawing objects are copied to the new area without transposing. 2377 // CopyFromClip is used to adjust the objects to the transposed block's 2378 // cell range area. 2379 // (mpDrawLayer in the original clipboard document is set only if there 2380 // are drawing objects to copy) 2381 2382 pTransClip->InitDrawLayer(); 2383 tools::Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(), 2384 aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i ); 2385 tools::Rectangle aDestRect = pTransClip->GetMMRect( 0, 0, 2386 static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()), 2387 static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i ); 2388 pTransClip->mpDrawLayer->CopyFromClip( mpDrawLayer.get(), i, aSourceRect, ScAddress(0,0,i), aDestRect ); 2389 } 2390 } 2391 2392 pTransClip->SetClipParam(GetClipParam()); 2393 pTransClip->GetClipParam().transpose(); 2394 } 2395 else 2396 { 2397 SAL_WARN("sc", "TransposeClip: Too big"); 2398 } 2399 2400 // This happens only when inserting... 2401 2402 GetClipParam().mbCutMode = false; 2403 } 2404 2405 namespace { 2406 2407 void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName, 2408 const sc::UpdatedRangeNames::NameIndicesType& rUsedNames) 2409 { 2410 pClipRangeName->clear(); 2411 for (const auto& rEntry : *pRangeName) //TODO: also DB and Pivot regions!!! 2412 { 2413 sal_uInt16 nIndex = rEntry.second->GetIndex(); 2414 bool bInUse = (rUsedNames.count(nIndex) > 0); 2415 if (!bInUse) 2416 continue; 2417 2418 ScRangeData* pData = new ScRangeData(*rEntry.second); 2419 if (pClipRangeName->insert(pData)) 2420 pData->SetIndex(nIndex); 2421 } 2422 } 2423 2424 } 2425 2426 void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks) 2427 { 2428 if (!pRangeName || pRangeName->empty()) 2429 return; 2430 2431 sc::UpdatedRangeNames aUsedNames; // indexes of named ranges that are used in the copied cells 2432 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pClipDoc->maTabs.size())); 2433 for (SCTAB i = 0; i < nMinSizeBothTabs; ++i) 2434 if (maTabs[i] && pClipDoc->maTabs[i]) 2435 if ( !pMarks || pMarks->GetTableSelect(i) ) 2436 maTabs[i]->FindRangeNamesInUse( 2437 rClipRange.aStart.Col(), rClipRange.aStart.Row(), 2438 rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames); 2439 2440 /* TODO: handle also sheet-local names */ 2441 sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1)); 2442 copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames); 2443 } 2444 2445 ScDocument::NumFmtMergeHandler::NumFmtMergeHandler(ScDocument* pDoc, const ScDocument* pSrcDoc) : 2446 mpDoc(pDoc) 2447 { 2448 mpDoc->MergeNumberFormatter(pSrcDoc); 2449 } 2450 2451 ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler() 2452 { 2453 ScMutationGuard aGuard(mpDoc, ScMutationGuardFlags::CORE); 2454 mpDoc->pFormatExchangeList = nullptr; 2455 } 2456 2457 void ScDocument::PrepareFormulaCalc() 2458 { 2459 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 2460 mpFormulaGroupCxt.reset(); 2461 } 2462 2463 SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) 2464 { 2465 ScTable* pTab = FetchTable(rPos.Tab()); 2466 if (!pTab) 2467 return nullptr; 2468 2469 return pTab->GetBroadcaster(rPos.Col(), rPos.Row()); 2470 } 2471 2472 const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const 2473 { 2474 const ScTable* pTab = FetchTable(rPos.Tab()); 2475 if (!pTab) 2476 return nullptr; 2477 2478 return pTab->GetBroadcaster(rPos.Col(), rPos.Row()); 2479 } 2480 2481 void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength ) 2482 { 2483 ScTable* pTab = FetchTable(rTopPos.Tab()); 2484 if (!pTab || nLength <= 0) 2485 return; 2486 2487 pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1); 2488 } 2489 2490 #if DUMP_COLUMN_STORAGE 2491 void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const 2492 { 2493 const ScTable* pTab = FetchTable(nTab); 2494 if (!pTab) 2495 return; 2496 2497 pTab->DumpColumnStorage(nCol); 2498 } 2499 #endif 2500 2501 #if DEBUG_AREA_BROADCASTER 2502 void ScDocument::DumpAreaBroadcasters() const 2503 { 2504 if (pBASM) 2505 pBASM->Dump(); 2506 } 2507 #endif 2508 2509 bool ScDocument::TableExists( SCTAB nTab ) const 2510 { 2511 return ValidTab(nTab) && static_cast<size_t>(nTab) < maTabs.size() && maTabs[nTab]; 2512 } 2513 2514 ScTable* ScDocument::FetchTable( SCTAB nTab ) 2515 { 2516 if (!TableExists(nTab)) 2517 return nullptr; 2518 2519 return maTabs[nTab].get(); 2520 } 2521 2522 const ScTable* ScDocument::FetchTable( SCTAB nTab ) const 2523 { 2524 if (!TableExists(nTab)) 2525 return nullptr; 2526 2527 return maTabs[nTab].get(); 2528 } 2529 2530 ScColumnsRange ScDocument::GetColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const 2531 { 2532 if (!TableExists(nTab)) 2533 { 2534 std::vector<std::unique_ptr<ScColumn>> aEmptyVector; 2535 return ScColumnsRange(ScColumnsRange::Iterator(aEmptyVector.begin()), 2536 ScColumnsRange::Iterator(aEmptyVector.end())); 2537 } 2538 2539 return maTabs[nTab]->GetColumnsRange(nColBegin, nColEnd); 2540 } 2541 2542 void ScDocument::MergeNumberFormatter(const ScDocument* pSrcDoc) 2543 { 2544 SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable(); 2545 SvNumberFormatter* pOtherFormatter = pSrcDoc->mxPoolHelper->GetFormTable(); 2546 if (pOtherFormatter && pOtherFormatter != pThisFormatter) 2547 { 2548 SvNumberFormatterIndexTable* pExchangeList = 2549 pThisFormatter->MergeFormatter(*pOtherFormatter); 2550 if (!pExchangeList->empty()) 2551 { 2552 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 2553 pFormatExchangeList = pExchangeList; 2554 } 2555 } 2556 } 2557 2558 ScClipParam& ScDocument::GetClipParam() 2559 { 2560 if (!mpClipParam) 2561 mpClipParam.reset(new ScClipParam); 2562 2563 return *mpClipParam; 2564 } 2565 2566 void ScDocument::SetClipParam(const ScClipParam& rParam) 2567 { 2568 mpClipParam.reset(new ScClipParam(rParam)); 2569 } 2570 2571 bool ScDocument::IsClipboardSource() const 2572 { 2573 if (bIsClip || mpShell == nullptr) 2574 return false; 2575 2576 ScDocument* pClipDoc = ScModule::GetClipDoc(); 2577 return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() && 2578 mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool(); 2579 } 2580 2581 void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1, 2582 SCCOL nCol2, SCROW nRow2, 2583 const ScMarkData& rMark, InsertDeleteFlags nInsFlag ) 2584 { 2585 if (nInsFlag & InsertDeleteFlags::CONTENTS) 2586 { 2587 std::shared_ptr<sc::ColumnBlockPositionSet> pSet( 2588 new sc::ColumnBlockPositionSet(*this)); 2589 2590 sc::StartListeningContext aStartCxt(*this, pSet); 2591 sc::EndListeningContext aEndCxt(*this, pSet, nullptr); 2592 2593 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 2594 for (const auto& rTab : rMark) 2595 { 2596 if (rTab >= nMax) 2597 break; 2598 if (maTabs[rTab]) 2599 maTabs[rTab]->StartListeningFormulaCells(aStartCxt, aEndCxt, nCol1, nRow1, nCol2, nRow2); 2600 } 2601 } 2602 } 2603 2604 void ScDocument::SetDirtyFromClip( 2605 SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark, 2606 InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans ) 2607 { 2608 if (nInsFlag & InsertDeleteFlags::CONTENTS) 2609 { 2610 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 2611 for (const auto& rTab : rMark) 2612 { 2613 if (rTab >= nMax) 2614 break; 2615 if (maTabs[rTab]) 2616 maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans); 2617 } 2618 } 2619 } 2620 2621 bool ScDocument::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCTAB nTab, SCCOL nCol ) 2622 { 2623 if (!TableExists(nTab)) 2624 return false; 2625 2626 return maTabs[nTab]->InitColumnBlockPosition(rBlockPos, nCol); 2627 } 2628 2629 void ScDocument::CopyBlockFromClip( 2630 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 2631 const ScMarkData& rMark, SCCOL nDx, SCROW nDy ) 2632 { 2633 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs; 2634 SCTAB nTabEnd = rCxt.getTabEnd(); 2635 SCTAB nClipTab = 0; 2636 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++) 2637 { 2638 if (maTabs[i] && rMark.GetTableSelect(i) ) 2639 { 2640 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size()); 2641 2642 maTabs[i]->CopyFromClip( 2643 rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get()); 2644 2645 if (rCxt.getClipDoc()->mpDrawLayer && (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS)) 2646 { 2647 // also copy drawing objects 2648 2649 // drawing layer must be created before calling CopyFromClip 2650 // (ScDocShell::MakeDrawLayer also does InitItems etc.) 2651 OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" ); 2652 if ( mpDrawLayer ) 2653 { 2654 // For GetMMRect, the row heights in the target document must already be valid 2655 // (copied in an extra step before pasting, or updated after pasting cells, but 2656 // before pasting objects). 2657 2658 tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect( 2659 nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab ); 2660 tools::Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i ); 2661 mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRect, 2662 ScAddress( nCol1, nRow1, i ), aDestRect ); 2663 } 2664 } 2665 2666 nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size()); 2667 } 2668 } 2669 if (rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS) 2670 { 2671 nClipTab = 0; 2672 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++) 2673 { 2674 if (maTabs[i] && rMark.GetTableSelect(i) ) 2675 { 2676 while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size()); 2677 SCTAB nDz = i - nClipTab; 2678 2679 // ranges of consecutive selected tables (in clipboard and dest. doc) 2680 // must be handled in one UpdateReference call 2681 SCTAB nFollow = 0; 2682 while ( i + nFollow < nTabEnd 2683 && rMark.GetTableSelect( i + nFollow + 1 ) 2684 && nClipTab + nFollow < MAXTAB 2685 && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] ) 2686 ++nFollow; 2687 2688 sc::RefUpdateContext aRefCxt(*this); 2689 aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow); 2690 aRefCxt.mnColDelta = nDx; 2691 aRefCxt.mnRowDelta = nDy; 2692 aRefCxt.mnTabDelta = nDz; 2693 if (rCxt.getClipDoc()->GetClipParam().mbCutMode) 2694 { 2695 // Update references only if cut originates from the same 2696 // document we are pasting into. 2697 if (rCxt.getClipDoc()->GetPool() == GetPool()) 2698 { 2699 bool bOldInserting = IsInsertingFromOtherDoc(); 2700 SetInsertingFromOtherDoc( true); 2701 aRefCxt.meMode = URM_MOVE; 2702 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false); 2703 2704 // For URM_MOVE group listeners may have been removed, 2705 // re-establish them. 2706 if (!aRefCxt.maRegroupCols.empty()) 2707 { 2708 /* TODO: holding the ColumnSet in a shared_ptr at 2709 * RefUpdateContext would eliminate the need of 2710 * copying it here. */ 2711 std::shared_ptr<const sc::ColumnSet> pColSet( new sc::ColumnSet( aRefCxt.maRegroupCols)); 2712 StartNeededListeners( pColSet); 2713 } 2714 2715 SetInsertingFromOtherDoc( bOldInserting); 2716 } 2717 } 2718 else 2719 { 2720 aRefCxt.meMode = URM_COPY; 2721 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false); 2722 } 2723 2724 nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size()); 2725 i = sal::static_int_cast<SCTAB>( i + nFollow ); 2726 } 2727 } 2728 } 2729 } 2730 2731 void ScDocument::CopyNonFilteredFromClip( 2732 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 2733 const ScMarkData& rMark, SCCOL nDx, SCROW & rClipStartRow ) 2734 { 2735 // call CopyBlockFromClip for ranges of consecutive non-filtered rows 2736 // nCol1/nRow1 etc. is in target doc 2737 2738 // filtered state is taken from first used table in clipboard (as in GetClipArea) 2739 SCTAB nFlagTab = 0; 2740 TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs; 2741 while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] ) 2742 ++nFlagTab; 2743 2744 SCROW nSourceRow = rClipStartRow; 2745 SCROW nSourceEnd = 0; 2746 if (!rCxt.getClipDoc()->GetClipParam().maRanges.empty()) 2747 nSourceEnd = rCxt.getClipDoc()->GetClipParam().maRanges.front().aEnd.Row(); 2748 SCROW nDestRow = nRow1; 2749 2750 while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 ) 2751 { 2752 // skip filtered rows 2753 nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab); 2754 2755 if ( nSourceRow <= nSourceEnd ) 2756 { 2757 // look for more non-filtered rows following 2758 SCROW nLastRow = nSourceRow; 2759 (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow); 2760 SCROW nFollow = nLastRow - nSourceRow; 2761 2762 if (nFollow > nSourceEnd - nSourceRow) 2763 nFollow = nSourceEnd - nSourceRow; 2764 if (nFollow > nRow2 - nDestRow) 2765 nFollow = nRow2 - nDestRow; 2766 2767 SCROW nNewDy = nDestRow - nSourceRow; 2768 CopyBlockFromClip( 2769 rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy); 2770 2771 nSourceRow += nFollow + 1; 2772 nDestRow += nFollow + 1; 2773 } 2774 } 2775 rClipStartRow = nSourceRow; 2776 } 2777 2778 namespace { 2779 2780 class BroadcastAction : public sc::ColumnSpanSet::ColumnAction 2781 { 2782 ScDocument& mrDoc; 2783 ScColumn* mpCol; 2784 2785 public: 2786 explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {} 2787 2788 virtual void startColumn( ScColumn* pCol ) override 2789 { 2790 mpCol = pCol; 2791 } 2792 2793 virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override 2794 { 2795 if (!bVal) 2796 return; 2797 2798 assert(mpCol); 2799 ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab()); 2800 aRange.aEnd.SetRow(nRow2); 2801 mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged); 2802 }; 2803 }; 2804 2805 } 2806 2807 void ScDocument::CopyFromClip( const ScRange& rDestRange, const ScMarkData& rMark, 2808 InsertDeleteFlags nInsFlag, 2809 ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut, 2810 bool bAsLink, bool bIncludeFiltered, bool bSkipAttrForEmpty, 2811 const ScRangeList * pDestRanges ) 2812 { 2813 if (bIsClip) 2814 return; 2815 2816 if (!pClipDoc) 2817 { 2818 OSL_FAIL("CopyFromClip: no ClipDoc"); 2819 pClipDoc = ScModule::GetClipDoc(); 2820 } 2821 2822 if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount()) 2823 return; 2824 2825 sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc. 2826 2827 NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc); 2828 2829 SCCOL nAllCol1 = rDestRange.aStart.Col(); 2830 SCROW nAllRow1 = rDestRange.aStart.Row(); 2831 SCCOL nAllCol2 = rDestRange.aEnd.Col(); 2832 SCROW nAllRow2 = rDestRange.aEnd.Row(); 2833 2834 SCCOL nXw = 0; 2835 SCROW nYw = 0; 2836 ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange(); 2837 for (SCTAB nTab = 0; nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()); nTab++) // find largest merge overlap 2838 if (pClipDoc->maTabs[nTab]) // all sheets of the clipboard content 2839 { 2840 SCCOL nThisEndX = aClipRange.aEnd.Col(); 2841 SCROW nThisEndY = aClipRange.aEnd.Row(); 2842 pClipDoc->ExtendMerge( aClipRange.aStart.Col(), 2843 aClipRange.aStart.Row(), 2844 nThisEndX, nThisEndY, nTab ); 2845 // only extra value from ExtendMerge 2846 nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() ); 2847 nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() ); 2848 if ( nThisEndX > nXw ) 2849 nXw = nThisEndX; 2850 if ( nThisEndY > nYw ) 2851 nYw = nThisEndY; 2852 } 2853 2854 SCCOL nDestAddX; 2855 SCROW nDestAddY; 2856 pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered ); 2857 nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX ); 2858 nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value 2859 2860 /* Decide which contents to delete before copying. Delete all 2861 contents if nInsFlag contains any real content flag. 2862 #i102056# Notes are pasted from clipboard in a second pass, 2863 together with the special flag InsertDeleteFlags::ADDNOTES that states to not 2864 overwrite/delete existing cells but to insert the notes into 2865 these cells. In this case, just delete old notes from the 2866 destination area. */ 2867 InsertDeleteFlags nDelFlag = InsertDeleteFlags::NONE; 2868 if ( (nInsFlag & (InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ADDNOTES)) == (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES) ) 2869 nDelFlag |= InsertDeleteFlags::NOTE; 2870 else if ( nInsFlag & InsertDeleteFlags::CONTENTS ) 2871 nDelFlag |= InsertDeleteFlags::CONTENTS; 2872 2873 if (nInsFlag & InsertDeleteFlags::ATTRIB) 2874 nDelFlag |= InsertDeleteFlags::ATTRIB; 2875 2876 sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty); 2877 std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark); 2878 aCxt.setTabRange(aTabRanges.first, aTabRanges.second); 2879 aCxt.setDeleteFlag(nDelFlag); 2880 2881 ScRangeList aLocalRangeList; 2882 if (!pDestRanges) 2883 { 2884 aLocalRangeList.push_back( rDestRange); 2885 pDestRanges = &aLocalRangeList; 2886 } 2887 2888 bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert 2889 2890 sc::ColumnSpanSet aBroadcastSpans(false); 2891 2892 SCCOL nClipStartCol = aClipRange.aStart.Col(); 2893 SCROW nClipStartRow = aClipRange.aStart.Row(); 2894 SCROW nClipEndRow = aClipRange.aEnd.Row(); 2895 for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange ) 2896 { 2897 const ScRange & rRange = (*pDestRanges)[nRange]; 2898 SCCOL nCol1 = rRange.aStart.Col(); 2899 SCROW nRow1 = rRange.aStart.Row(); 2900 SCCOL nCol2 = rRange.aEnd.Col(); 2901 SCROW nRow2 = rRange.aEnd.Row(); 2902 2903 if (bSkipAttrForEmpty) 2904 { 2905 // Delete cells in the destination only if their corresponding clip cells are not empty. 2906 aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2); 2907 DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans); 2908 } 2909 else 2910 DeleteArea(nCol1, nRow1, nCol2, nRow2, rMark, nDelFlag, false, &aBroadcastSpans); 2911 2912 if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2)) 2913 continue; 2914 2915 SCCOL nC1 = nCol1; 2916 SCROW nR1 = nRow1; 2917 SCCOL nC2 = nC1 + nXw; 2918 if (nC2 > nCol2) 2919 nC2 = nCol2; 2920 SCROW nR2 = nR1 + nYw; 2921 if (nR2 > nRow2) 2922 nR2 = nRow2; 2923 2924 const SCCOLROW nThreshold = 8192; 2925 bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold)); 2926 std::vector< SCTAB > vTables; 2927 2928 if (bPreallocatePattern) 2929 { 2930 for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i) 2931 if (maTabs[i] && rMark.GetTableSelect( i ) ) 2932 vTables.push_back( i ); 2933 } 2934 2935 do 2936 { 2937 // Pasting is done column-wise, when pasting to a filtered 2938 // area this results in partitioning and we have to 2939 // remember and reset the start row for each column until 2940 // it can be advanced for the next chunk of unfiltered 2941 // rows. 2942 SCROW nSaveClipStartRow = nClipStartRow; 2943 do 2944 { 2945 nClipStartRow = nSaveClipStartRow; 2946 SCCOL nDx = nC1 - nClipStartCol; 2947 SCROW nDy = nR1 - nClipStartRow; 2948 if ( bIncludeFiltered ) 2949 { 2950 CopyBlockFromClip( 2951 aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy); 2952 nClipStartRow += nR2 - nR1 + 1; 2953 } 2954 else 2955 { 2956 CopyNonFilteredFromClip( 2957 aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow); 2958 } 2959 nC1 = nC2 + 1; 2960 nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2); 2961 } while (nC1 <= nCol2); 2962 if (nClipStartRow > nClipEndRow) 2963 nClipStartRow = aClipRange.aStart.Row(); 2964 nC1 = nCol1; 2965 nC2 = nC1 + nXw; 2966 if (nC2 > nCol2) 2967 nC2 = nCol2; 2968 2969 // Preallocate pattern memory once if further chunks are to be pasted. 2970 if (bPreallocatePattern && (nR2+1) <= nRow2) 2971 { 2972 SCROW nR3 = nR2 + 1; 2973 for (SCTAB nTab : vTables) 2974 { 2975 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 2976 { 2977 // Pattern count of the first chunk pasted. 2978 SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2); 2979 // If it is only one pattern per chunk and chunks are 2980 // pasted consecutively then it will get its range 2981 // enlarged for each chunk and no further allocation 2982 // happens. For non-consecutive chunks we're out of 2983 // luck in this case. 2984 if (nChunk > 1) 2985 { 2986 SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1); 2987 SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2); 2988 if (nNeeded > nRemain) 2989 { 2990 SCSIZE nCurr = GetPatternCount( nTab, nCol); 2991 ReservePatternCount( nTab, nCol, nCurr + nNeeded); 2992 } 2993 } 2994 } 2995 } 2996 bPreallocatePattern = false; 2997 } 2998 2999 nR1 = nR2 + 1; 3000 nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2); 3001 } while (nR1 <= nRow2); 3002 } 3003 3004 bInsertingFromOtherDoc = false; 3005 3006 // Create Listener after everything has been inserted 3007 StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag ); 3008 3009 { 3010 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged); 3011 3012 // Set all formula cells dirty, and collect non-empty non-formula cell 3013 // positions so that we can broadcast on them below. 3014 SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans); 3015 3016 BroadcastAction aAction(*this); 3017 aBroadcastSpans.executeColumnAction(*this, aAction); 3018 } 3019 3020 if (bResetCut) 3021 pClipDoc->GetClipParam().mbCutMode = false; 3022 } 3023 3024 void ScDocument::CopyMultiRangeFromClip( 3025 const ScAddress& rDestPos, const ScMarkData& rMark, InsertDeleteFlags nInsFlag, ScDocument* pClipDoc, 3026 bool bResetCut, bool bAsLink, bool /*bIncludeFiltered*/, bool bSkipAttrForEmpty) 3027 { 3028 if (bIsClip) 3029 return; 3030 3031 if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount()) 3032 // There is nothing in the clip doc to copy. 3033 return; 3034 3035 // Right now, we don't allow pasting into filtered rows, so we don't even handle it here. 3036 3037 sc::AutoCalcSwitch aACSwitch(*this, false); // turn of auto calc temporarily. 3038 NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc); 3039 3040 ScRange aDestRange; 3041 rMark.GetMarkArea(aDestRange); 3042 3043 bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert 3044 3045 SCCOL nCol1 = rDestPos.Col(); 3046 SCROW nRow1 = rDestPos.Row(); 3047 ScClipParam& rClipParam = pClipDoc->GetClipParam(); 3048 3049 sc::ColumnSpanSet aBroadcastSpans(false); 3050 3051 if (!bSkipAttrForEmpty) 3052 { 3053 // Do the deletion first. 3054 SCCOL nColSize = rClipParam.getPasteColSize(); 3055 SCROW nRowSize = rClipParam.getPasteRowSize(); 3056 3057 DeleteArea(nCol1, nRow1, nCol1+nColSize-1, nRow1+nRowSize-1, rMark, InsertDeleteFlags::CONTENTS, false, &aBroadcastSpans); 3058 } 3059 3060 sc::CopyFromClipContext aCxt(*this, nullptr, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty); 3061 std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark); 3062 aCxt.setTabRange(aTabRanges.first, aTabRanges.second); 3063 3064 for (size_t i = 0, n = rClipParam.maRanges.size(); i < n; ++i) 3065 { 3066 const ScRange & rRange = rClipParam.maRanges[i]; 3067 3068 SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1; 3069 SCCOL nDx = static_cast<SCCOL>(nCol1 - rRange.aStart.Col()); 3070 SCROW nDy = static_cast<SCROW>(nRow1 - rRange.aStart.Row()); 3071 SCCOL nCol2 = nCol1 + rRange.aEnd.Col() - rRange.aStart.Col(); 3072 SCROW nEndRow = nRow1 + nRowCount - 1; 3073 3074 CopyBlockFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx, nDy); 3075 3076 switch (rClipParam.meDirection) 3077 { 3078 case ScClipParam::Row: 3079 // Begin row for the next range being pasted. 3080 nRow1 += nRowCount; 3081 break; 3082 case ScClipParam::Column: 3083 nCol1 += rRange.aEnd.Col() - rRange.aStart.Col() + 1; 3084 break; 3085 default: 3086 ; 3087 } 3088 } 3089 3090 bInsertingFromOtherDoc = false; 3091 3092 // Create Listener after everything has been inserted 3093 StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(), 3094 aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag ); 3095 3096 { 3097 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged); 3098 3099 // Set formula cells dirty and collect non-formula cells. 3100 SetDirtyFromClip( 3101 aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), 3102 rMark, nInsFlag, aBroadcastSpans); 3103 3104 BroadcastAction aAction(*this); 3105 aBroadcastSpans.executeColumnAction(*this, aAction); 3106 } 3107 3108 if (bResetCut) 3109 pClipDoc->GetClipParam().mbCutMode = false; 3110 } 3111 3112 void ScDocument::SetClipArea( const ScRange& rArea, bool bCut ) 3113 { 3114 if (bIsClip) 3115 { 3116 ScClipParam& rClipParam = GetClipParam(); 3117 rClipParam.maRanges.RemoveAll(); 3118 rClipParam.maRanges.push_back(rArea); 3119 rClipParam.mbCutMode = bCut; 3120 } 3121 else 3122 { 3123 OSL_FAIL("SetClipArea: No Clip"); 3124 } 3125 } 3126 3127 void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, bool bIncludeFiltered) 3128 { 3129 if (!bIsClip) 3130 { 3131 OSL_FAIL("GetClipArea: No Clip"); 3132 return; 3133 } 3134 3135 ScRangeList& rClipRanges = GetClipParam().maRanges; 3136 if (rClipRanges.empty()) 3137 // No clip range. Bail out. 3138 return; 3139 3140 ScRange const & rRange = rClipRanges.front(); 3141 SCCOL nStartCol = rRange.aStart.Col(); 3142 SCCOL nEndCol = rRange.aEnd.Col(); 3143 SCROW nStartRow = rRange.aStart.Row(); 3144 SCROW nEndRow = rRange.aEnd.Row(); 3145 for ( size_t i = 1, n = rClipRanges.size(); i < n; ++i ) 3146 { 3147 ScRange const & rRange2 = rClipRanges[ i ]; 3148 if (rRange2.aStart.Col() < nStartCol) 3149 nStartCol = rRange2.aStart.Col(); 3150 if (rRange2.aStart.Row() < nStartRow) 3151 nStartRow = rRange2.aStart.Row(); 3152 if (rRange2.aEnd.Col() > nEndCol) 3153 nEndCol = rRange2.aEnd.Col(); 3154 if (rRange2.aEnd.Row() < nEndRow) 3155 nEndRow = rRange2.aEnd.Row(); 3156 } 3157 3158 nClipX = nEndCol - nStartCol; 3159 3160 if ( bIncludeFiltered ) 3161 nClipY = nEndRow - nStartRow; 3162 else 3163 { 3164 // count non-filtered rows 3165 // count on first used table in clipboard 3166 SCTAB nCountTab = 0; 3167 while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] ) 3168 ++nCountTab; 3169 3170 SCROW nResult = CountNonFilteredRows(nStartRow, nEndRow, nCountTab); 3171 3172 if ( nResult > 0 ) 3173 nClipY = nResult - 1; 3174 else 3175 nClipY = 0; // always return at least 1 row 3176 } 3177 } 3178 3179 void ScDocument::GetClipStart(SCCOL& nClipX, SCROW& nClipY) 3180 { 3181 if (bIsClip) 3182 { 3183 ScRangeList& rClipRanges = GetClipParam().maRanges; 3184 if ( !rClipRanges.empty() ) 3185 { 3186 nClipX = rClipRanges.front().aStart.Col(); 3187 nClipY = rClipRanges.front().aStart.Row(); 3188 } 3189 } 3190 else 3191 { 3192 OSL_FAIL("GetClipStart: No Clip"); 3193 } 3194 } 3195 3196 bool ScDocument::HasClipFilteredRows() 3197 { 3198 // count on first used table in clipboard 3199 SCTAB nCountTab = 0; 3200 while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] ) 3201 ++nCountTab; 3202 3203 ScRangeList& rClipRanges = GetClipParam().maRanges; 3204 if ( rClipRanges.empty() ) 3205 return false; 3206 3207 for ( size_t i = 0, n = rClipRanges.size(); i < n; ++i ) 3208 { 3209 ScRange & rRange = rClipRanges[ i ]; 3210 bool bAnswer = maTabs[nCountTab]->HasFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row()); 3211 if (bAnswer) 3212 return true; 3213 } 3214 return false; 3215 } 3216 3217 void ScDocument::MixDocument( const ScRange& rRange, ScPasteFunc nFunction, bool bSkipEmpty, 3218 ScDocument* pSrcDoc ) 3219 { 3220 SCTAB nTab1 = rRange.aStart.Tab(); 3221 SCTAB nTab2 = rRange.aEnd.Tab(); 3222 sc::MixDocContext aCxt(*this); 3223 SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pSrcDoc->maTabs.size())); 3224 for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++) 3225 { 3226 ScTable* pTab = FetchTable(i); 3227 const ScTable* pSrcTab = pSrcDoc->FetchTable(i); 3228 if (!pTab || !pSrcTab) 3229 continue; 3230 3231 pTab->MixData( 3232 aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), 3233 nFunction, bSkipEmpty, pSrcTab); 3234 } 3235 } 3236 3237 void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark, 3238 InsertDeleteFlags nFlags, ScPasteFunc nFunction, 3239 bool bSkipEmpty, bool bAsLink ) 3240 { 3241 InsertDeleteFlags nDelFlags = nFlags; 3242 if (nDelFlags & InsertDeleteFlags::CONTENTS) 3243 nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing! 3244 3245 SCTAB nSrcTab = rSrcArea.aStart.Tab(); 3246 3247 if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab]) 3248 { 3249 SCCOL nStartCol = rSrcArea.aStart.Col(); 3250 SCROW nStartRow = rSrcArea.aStart.Row(); 3251 SCCOL nEndCol = rSrcArea.aEnd.Col(); 3252 SCROW nEndRow = rSrcArea.aEnd.Row(); 3253 ScDocumentUniquePtr pMixDoc; 3254 bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS ); 3255 3256 bool bOldAutoCalc = GetAutoCalc(); 3257 SetAutoCalc( false ); // avoid multiple calculations 3258 3259 sc::CopyToDocContext aCxt(*this); 3260 sc::MixDocContext aMixDocCxt(*this); 3261 3262 SCTAB nCount = static_cast<SCTAB>(maTabs.size()); 3263 for (const SCTAB& i : rMark) 3264 { 3265 if (i >= nCount) 3266 break; 3267 if (i != nSrcTab && maTabs[i]) 3268 { 3269 if (bDoMix) 3270 { 3271 if (!pMixDoc) 3272 { 3273 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 3274 pMixDoc->InitUndo( this, i, i ); 3275 } 3276 else 3277 pMixDoc->AddUndoTab( i, i ); 3278 3279 // context used for copying content to the temporary mix document. 3280 sc::CopyToDocContext aMixCxt(*pMixDoc); 3281 maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow, 3282 InsertDeleteFlags::CONTENTS, false, pMixDoc->maTabs[i].get(), 3283 /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true, 3284 /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true ); 3285 } 3286 maTabs[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags); 3287 maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow, 3288 nFlags, false, maTabs[i].get(), nullptr, bAsLink, 3289 /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true ); 3290 3291 if (bDoMix) 3292 maTabs[i]->MixData(aMixDocCxt, nStartCol,nStartRow, nEndCol,nEndRow, 3293 nFunction, bSkipEmpty, pMixDoc->maTabs[i].get() ); 3294 } 3295 } 3296 3297 SetAutoCalc( bOldAutoCalc ); 3298 } 3299 else 3300 { 3301 OSL_FAIL("wrong table"); 3302 } 3303 } 3304 3305 void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark, 3306 InsertDeleteFlags nFlags, ScPasteFunc nFunction, 3307 bool bSkipEmpty, bool bAsLink ) 3308 { 3309 InsertDeleteFlags nDelFlags = nFlags; 3310 if (nDelFlags & InsertDeleteFlags::CONTENTS) 3311 nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing! 3312 3313 if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab]) 3314 { 3315 ScDocumentUniquePtr pMixDoc; 3316 bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS ); 3317 3318 bool bOldAutoCalc = GetAutoCalc(); 3319 SetAutoCalc( false ); // avoid multiple calculations 3320 3321 ScRange aArea; 3322 rMark.GetMultiMarkArea( aArea ); 3323 SCCOL nStartCol = aArea.aStart.Col(); 3324 SCROW nStartRow = aArea.aStart.Row(); 3325 SCCOL nEndCol = aArea.aEnd.Col(); 3326 SCROW nEndRow = aArea.aEnd.Row(); 3327 3328 sc::CopyToDocContext aCxt(*this); 3329 sc::MixDocContext aMixDocCxt(*this); 3330 SCTAB nCount = static_cast<SCTAB>(maTabs.size()); 3331 for (const SCTAB& i : rMark) 3332 { 3333 if (i >= nCount) 3334 break; 3335 if ( i != nSrcTab && maTabs[i] ) 3336 { 3337 if (bDoMix) 3338 { 3339 if (!pMixDoc) 3340 { 3341 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 3342 pMixDoc->InitUndo( this, i, i ); 3343 } 3344 else 3345 pMixDoc->AddUndoTab( i, i ); 3346 3347 sc::CopyToDocContext aMixCxt(*pMixDoc); 3348 maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow, 3349 InsertDeleteFlags::CONTENTS, true, pMixDoc->maTabs[i].get(), &rMark, 3350 /*bAsLink*/false, /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, 3351 /*bCopyCaptions*/true ); 3352 } 3353 3354 maTabs[i]->DeleteSelection( nDelFlags, rMark ); 3355 maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow, 3356 nFlags, true, maTabs[i].get(), &rMark, bAsLink, 3357 /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true ); 3358 3359 if (bDoMix) 3360 maTabs[i]->MixMarked(aMixDocCxt, rMark, nFunction, bSkipEmpty, pMixDoc->maTabs[i].get()); 3361 } 3362 } 3363 3364 SetAutoCalc( bOldAutoCalc ); 3365 } 3366 else 3367 { 3368 OSL_FAIL("wrong table"); 3369 } 3370 } 3371 3372 bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString, 3373 const ScSetStringParam* pParam ) 3374 { 3375 ScTable* pTab = FetchTable(nTab); 3376 if (!pTab) 3377 return false; 3378 3379 const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(nCol, nRow); 3380 if (pCurCellFormula && pCurCellFormula->IsShared()) 3381 { 3382 // In case setting this string affects an existing formula group, end 3383 // its listening to purge then empty cell broadcasters. Affected 3384 // remaining split group listeners will be set up again via 3385 // ScColumn::DetachFormulaCell() and 3386 // ScColumn::StartListeningUnshared(). 3387 3388 sc::EndListeningContext aCxt(*this); 3389 ScAddress aPos(nCol, nRow, nTab); 3390 EndListeningIntersectedGroup(aCxt, aPos, nullptr); 3391 aCxt.purgeEmptyBroadcasters(); 3392 } 3393 3394 return pTab->SetString(nCol, nRow, nTab, rString, pParam); 3395 } 3396 3397 bool ScDocument::SetString( 3398 const ScAddress& rPos, const OUString& rString, const ScSetStringParam* pParam ) 3399 { 3400 return SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rString, pParam); 3401 } 3402 3403 bool ScDocument::SetEditText( const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText ) 3404 { 3405 if (!TableExists(rPos.Tab())) 3406 { 3407 return false; 3408 } 3409 3410 return maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), std::move(pEditText)); 3411 } 3412 3413 void ScDocument::SetEditText( const ScAddress& rPos, const EditTextObject& rEditText, const SfxItemPool* pEditPool ) 3414 { 3415 if (!TableExists(rPos.Tab())) 3416 return; 3417 3418 maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEditText, pEditPool); 3419 } 3420 3421 void ScDocument::SetEditText( const ScAddress& rPos, const OUString& rStr ) 3422 { 3423 if (!TableExists(rPos.Tab())) 3424 return; 3425 3426 ScFieldEditEngine& rEngine = GetEditEngine(); 3427 rEngine.SetText(rStr); 3428 maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject()); 3429 } 3430 3431 SCROW ScDocument::GetFirstEditTextRow( const ScRange& rRange ) const 3432 { 3433 const ScTable* pTab = FetchTable(rRange.aStart.Tab()); 3434 if (!pTab) 3435 return -1; 3436 3437 return pTab->GetFirstEditTextRow(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 3438 } 3439 3440 void ScDocument::SetTextCell( const ScAddress& rPos, const OUString& rStr ) 3441 { 3442 if (!TableExists(rPos.Tab())) 3443 return; 3444 3445 if (ScStringUtil::isMultiline(rStr)) 3446 { 3447 ScFieldEditEngine& rEngine = GetEditEngine(); 3448 rEngine.SetText(rStr); 3449 maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject()); 3450 } 3451 else 3452 { 3453 ScSetStringParam aParam; 3454 aParam.setTextInput(); 3455 maTabs[rPos.Tab()]->SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rStr, &aParam); 3456 } 3457 } 3458 3459 void ScDocument::SetEmptyCell( const ScAddress& rPos ) 3460 { 3461 if (!TableExists(rPos.Tab())) 3462 return; 3463 3464 maTabs[rPos.Tab()]->SetEmptyCell(rPos.Col(), rPos.Row()); 3465 } 3466 3467 void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal ) 3468 { 3469 SetValue(ScAddress(nCol, nRow, nTab), rVal); 3470 } 3471 3472 void ScDocument::SetValue( const ScAddress& rPos, double fVal ) 3473 { 3474 ScTable* pTab = FetchTable(rPos.Tab()); 3475 if (!pTab) 3476 return; 3477 3478 const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(rPos.Col(), rPos.Row()); 3479 if (pCurCellFormula && pCurCellFormula->IsShared()) 3480 { 3481 // In case setting this value affects an existing formula group, end 3482 // its listening to purge then empty cell broadcasters. Affected 3483 // remaining split group listeners will be set up again via 3484 // ScColumn::DetachFormulaCell() and 3485 // ScColumn::StartListeningUnshared(). 3486 3487 sc::EndListeningContext aCxt(*this); 3488 EndListeningIntersectedGroup(aCxt, rPos, nullptr); 3489 aCxt.purgeEmptyBroadcasters(); 3490 } 3491 3492 pTab->SetValue(rPos.Col(), rPos.Row(), fVal); 3493 } 3494 3495 OUString ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext* pContext ) const 3496 { 3497 if (TableExists(nTab)) 3498 { 3499 OUString aStr; 3500 maTabs[nTab]->GetString(nCol, nRow, aStr, pContext); 3501 return aStr; 3502 } 3503 else 3504 return EMPTY_OUSTRING; 3505 } 3506 3507 OUString ScDocument::GetString( const ScAddress& rPos, const ScInterpreterContext* pContext ) const 3508 { 3509 if (!TableExists(rPos.Tab())) 3510 return EMPTY_OUSTRING; 3511 3512 OUString aStr; 3513 maTabs[rPos.Tab()]->GetString(rPos.Col(), rPos.Row(), aStr, pContext); 3514 return aStr; 3515 } 3516 3517 double* ScDocument::GetValueCell( const ScAddress& rPos ) 3518 { 3519 if (!TableExists(rPos.Tab())) 3520 return nullptr; 3521 3522 return maTabs[rPos.Tab()]->GetValueCell(rPos.Col(), rPos.Row()); 3523 } 3524 3525 svl::SharedString ScDocument::GetSharedString( const ScAddress& rPos ) const 3526 { 3527 if (!TableExists(rPos.Tab())) 3528 return svl::SharedString(); 3529 3530 return maTabs[rPos.Tab()]->GetSharedString(rPos.Col(), rPos.Row()); 3531 } 3532 3533 std::shared_ptr<sc::FormulaGroupContext>& ScDocument::GetFormulaGroupContext() 3534 { 3535 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 3536 if (!mpFormulaGroupCxt) 3537 mpFormulaGroupCxt.reset(new sc::FormulaGroupContext); 3538 3539 return mpFormulaGroupCxt; 3540 } 3541 3542 void ScDocument::DiscardFormulaGroupContext() 3543 { 3544 assert(!IsThreadedGroupCalcInProgress()); 3545 if( !mbFormulaGroupCxtBlockDiscard ) 3546 mpFormulaGroupCxt.reset(); 3547 } 3548 3549 void ScDocument::GetInputString( SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rString ) 3550 { 3551 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3552 maTabs[nTab]->GetInputString( nCol, nRow, rString ); 3553 else 3554 rString.clear(); 3555 } 3556 3557 FormulaError ScDocument::GetStringForFormula( const ScAddress& rPos, OUString& rString ) 3558 { 3559 // Used in formulas (add-in parameters etc), so it must use the same semantics as 3560 // ScInterpreter::GetCellString: always format values as numbers. 3561 // The return value is the error code. 3562 3563 ScRefCellValue aCell(*this, rPos); 3564 if (aCell.isEmpty()) 3565 { 3566 rString = EMPTY_OUSTRING; 3567 return FormulaError::NONE; 3568 } 3569 3570 FormulaError nErr = FormulaError::NONE; 3571 OUString aStr; 3572 SvNumberFormatter* pFormatter = GetFormatTable(); 3573 switch (aCell.meType) 3574 { 3575 case CELLTYPE_STRING: 3576 case CELLTYPE_EDIT: 3577 aStr = aCell.getString(this); 3578 break; 3579 case CELLTYPE_FORMULA: 3580 { 3581 ScFormulaCell* pFCell = aCell.mpFormula; 3582 nErr = pFCell->GetErrCode(); 3583 if (pFCell->IsValue()) 3584 { 3585 double fVal = pFCell->GetValue(); 3586 sal_uInt32 nIndex = pFormatter->GetStandardFormat( 3587 SvNumFormatType::NUMBER, 3588 ScGlobal::eLnge); 3589 pFormatter->GetInputLineString(fVal, nIndex, aStr); 3590 } 3591 else 3592 aStr = pFCell->GetString().getString(); 3593 } 3594 break; 3595 case CELLTYPE_VALUE: 3596 { 3597 double fVal = aCell.mfValue; 3598 sal_uInt32 nIndex = pFormatter->GetStandardFormat( 3599 SvNumFormatType::NUMBER, 3600 ScGlobal::eLnge); 3601 pFormatter->GetInputLineString(fVal, nIndex, aStr); 3602 } 3603 break; 3604 default: 3605 ; 3606 } 3607 3608 rString = aStr; 3609 return nErr; 3610 } 3611 3612 void ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, double& rValue ) const 3613 { 3614 if (TableExists(nTab)) 3615 rValue = maTabs[nTab]->GetValue( nCol, nRow ); 3616 else 3617 rValue = 0.0; 3618 } 3619 3620 const EditTextObject* ScDocument::GetEditText( const ScAddress& rPos ) const 3621 { 3622 SCTAB nTab = rPos.Tab(); 3623 if (!TableExists(nTab)) 3624 return nullptr; 3625 3626 return maTabs[nTab]->GetEditText(rPos.Col(), rPos.Row()); 3627 } 3628 3629 void ScDocument::RemoveEditTextCharAttribs( const ScAddress& rPos, const ScPatternAttr& rAttr ) 3630 { 3631 if (!TableExists(rPos.Tab())) 3632 return; 3633 3634 return maTabs[rPos.Tab()]->RemoveEditTextCharAttribs(rPos.Col(), rPos.Row(), rAttr); 3635 } 3636 3637 double ScDocument::GetValue( const ScAddress& rPos ) const 3638 { 3639 SCTAB nTab = rPos.Tab(); 3640 if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3641 return maTabs[nTab]->GetValue(rPos.Col(), rPos.Row()); 3642 return 0.0; 3643 } 3644 3645 double ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 3646 { 3647 ScAddress aAdr(nCol, nRow, nTab); return GetValue(aAdr); 3648 } 3649 3650 void ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab, 3651 sal_uInt32& rFormat ) const 3652 { 3653 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 3654 if (maTabs[nTab]) 3655 { 3656 rFormat = maTabs[nTab]->GetNumberFormat( nCol, nRow ); 3657 return ; 3658 } 3659 rFormat = 0; 3660 } 3661 3662 sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const 3663 { 3664 SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab(); 3665 SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col(); 3666 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row(); 3667 3668 if (!TableExists(nTab1) || !TableExists(nTab2)) 3669 return 0; 3670 3671 sal_uInt32 nFormat = 0; 3672 bool bFirstItem = true; 3673 for (SCTAB nTab = nTab1; nTab <= nTab2 && nTab < static_cast<SCTAB>(maTabs.size()) ; ++nTab) 3674 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) 3675 { 3676 sal_uInt32 nThisFormat = maTabs[nTab]->GetNumberFormat(nCol, nRow1, nRow2); 3677 if (bFirstItem) 3678 { 3679 nFormat = nThisFormat; 3680 bFirstItem = false; 3681 } 3682 else if (nThisFormat != nFormat) 3683 return 0; 3684 } 3685 3686 return nFormat; 3687 } 3688 3689 sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const 3690 { 3691 SCTAB nTab = rPos.Tab(); 3692 if (!TableExists(nTab)) 3693 return 0; 3694 3695 return maTabs[nTab]->GetNumberFormat( rContext, rPos ); 3696 } 3697 3698 void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat ) 3699 { 3700 assert(!IsThreadedGroupCalcInProgress()); 3701 SCTAB nTab = rPos.Tab(); 3702 if (!TableExists(nTab)) 3703 return; 3704 3705 maTabs[nTab]->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat); 3706 } 3707 3708 void ScDocument::GetNumberFormatInfo( const ScInterpreterContext& rContext, SvNumFormatType& nType, sal_uInt32& nIndex, 3709 const ScAddress& rPos ) const 3710 { 3711 SCTAB nTab = rPos.Tab(); 3712 if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3713 { 3714 nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos ); 3715 nType = rContext.GetFormatTable()->GetType( nIndex ); 3716 } 3717 else 3718 { 3719 nType = SvNumFormatType::UNDEFINED; 3720 nIndex = 0; 3721 } 3722 } 3723 3724 void ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rFormula ) const 3725 { 3726 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3727 maTabs[nTab]->GetFormula( nCol, nRow, rFormula ); 3728 else 3729 rFormula.clear(); 3730 } 3731 3732 const ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos ) const 3733 { 3734 if (!TableExists(rPos.Tab())) 3735 return nullptr; 3736 3737 return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row()); 3738 } 3739 3740 ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos ) 3741 { 3742 if (!TableExists(rPos.Tab())) 3743 return nullptr; 3744 3745 return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row()); 3746 } 3747 3748 CellType ScDocument::GetCellType( const ScAddress& rPos ) const 3749 { 3750 SCTAB nTab = rPos.Tab(); 3751 if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3752 return maTabs[nTab]->GetCellType( rPos ); 3753 return CELLTYPE_NONE; 3754 } 3755 3756 void ScDocument::GetCellType( SCCOL nCol, SCROW nRow, SCTAB nTab, 3757 CellType& rCellType ) const 3758 { 3759 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 3760 rCellType = maTabs[nTab]->GetCellType( nCol, nRow ); 3761 else 3762 rCellType = CELLTYPE_NONE; 3763 } 3764 3765 bool ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 3766 { 3767 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3768 return maTabs[nTab]->HasStringData( nCol, nRow ); 3769 else 3770 return false; 3771 } 3772 3773 bool ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 3774 { 3775 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 3776 return maTabs[nTab]->HasValueData( nCol, nRow ); 3777 else 3778 return false; 3779 } 3780 3781 bool ScDocument::HasValueData( const ScAddress& rPos ) const 3782 { 3783 return HasValueData(rPos.Col(), rPos.Row(), rPos.Tab()); 3784 } 3785 3786 bool ScDocument::HasStringCells( const ScRange& rRange ) const 3787 { 3788 // true, if String- or Edit cells in range 3789 3790 SCCOL nStartCol = rRange.aStart.Col(); 3791 SCROW nStartRow = rRange.aStart.Row(); 3792 SCTAB nStartTab = rRange.aStart.Tab(); 3793 SCCOL nEndCol = rRange.aEnd.Col(); 3794 SCROW nEndRow = rRange.aEnd.Row(); 3795 SCTAB nEndTab = rRange.aEnd.Tab(); 3796 3797 for ( SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ ) 3798 if ( maTabs[nTab] && maTabs[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) ) 3799 return true; 3800 3801 return false; 3802 } 3803 3804 bool ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 3805 { 3806 sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue(); 3807 if( nValidation ) 3808 { 3809 const ScValidationData* pData = GetValidationEntry( nValidation ); 3810 if( pData && pData->HasSelectionList() ) 3811 return true; 3812 } 3813 return HasStringCells( ScRange( nCol, 0, nTab, nCol, MAXROW, nTab ) ); 3814 } 3815 3816 bool ScDocument::HasValidationData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 3817 { 3818 sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue(); 3819 if( nValidation ) 3820 { 3821 const ScValidationData* pData = GetValidationEntry( nValidation ); 3822 if( pData && pData->GetDataMode() != ScValidationMode::SC_VALID_ANY ) 3823 return true; 3824 } 3825 return false; 3826 } 3827 3828 void ScDocument::CheckVectorizationState() 3829 { 3830 bool bOldAutoCalc = GetAutoCalc(); 3831 bAutoCalc = false; // no multiple calculations 3832 3833 for (const auto& a : maTabs) 3834 { 3835 if (a) 3836 a->CheckVectorizationState(); 3837 } 3838 3839 SetAutoCalc(bOldAutoCalc); 3840 } 3841 3842 void ScDocument::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt ) 3843 { 3844 bool bOldAutoCalc = GetAutoCalc(); 3845 bAutoCalc = false; // no multiple calculations 3846 { // scope for bulk broadcast 3847 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged); 3848 for (const auto& a : maTabs) 3849 { 3850 if (a) 3851 a->SetAllFormulasDirty(rCxt); 3852 } 3853 } 3854 3855 // Although Charts are also set to dirty in Tracking without AutoCalc 3856 // if all formulas are dirty, the charts can no longer be caught 3857 // (#45205#) - that is why all Charts have to be explicitly handled again 3858 if (pChartListenerCollection) 3859 pChartListenerCollection->SetDirty(); 3860 3861 SetAutoCalc( bOldAutoCalc ); 3862 } 3863 3864 void ScDocument::SetDirty( const ScRange& rRange, bool bIncludeEmptyCells ) 3865 { 3866 bool bOldAutoCalc = GetAutoCalc(); 3867 bAutoCalc = false; // no multiple calculations 3868 { // scope for bulk broadcast 3869 ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged); 3870 SCTAB nTab2 = rRange.aEnd.Tab(); 3871 for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++) 3872 if (maTabs[i]) maTabs[i]->SetDirty( rRange, 3873 (bIncludeEmptyCells ? ScColumn::BROADCAST_BROADCASTERS : ScColumn::BROADCAST_DATA_POSITIONS)); 3874 3875 /* TODO: this now also notifies conditional formatting and does an UNO 3876 * broadcast, which wasn't done here before. Is that an actually 3877 * desired side effect, or should we come up with a method that 3878 * doesn't? */ 3879 if (bIncludeEmptyCells) 3880 BroadcastCells( rRange, SfxHintId::ScDataChanged, false); 3881 } 3882 SetAutoCalc( bOldAutoCalc ); 3883 } 3884 3885 void ScDocument::SetTableOpDirty( const ScRange& rRange ) 3886 { 3887 bool bOldAutoCalc = GetAutoCalc(); 3888 bAutoCalc = false; // no multiple recalculation 3889 SCTAB nTab2 = rRange.aEnd.Tab(); 3890 for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++) 3891 if (maTabs[i]) maTabs[i]->SetTableOpDirty( rRange ); 3892 SetAutoCalc( bOldAutoCalc ); 3893 } 3894 3895 void ScDocument::InterpretDirtyCells( const ScRangeList& rRanges ) 3896 { 3897 if (!GetAutoCalc()) 3898 return; 3899 3900 PrepareFormulaCalc(); 3901 3902 for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++) 3903 { 3904 const ScRange& rRange = rRanges[nPos]; 3905 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) 3906 { 3907 ScTable* pTab = FetchTable(nTab); 3908 if (!pTab) 3909 return; 3910 3911 pTab->InterpretDirtyCells( 3912 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 3913 } 3914 } 3915 3916 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 3917 mpFormulaGroupCxt.reset(); 3918 } 3919 3920 void ScDocument::AddTableOpFormulaCell( ScFormulaCell* pCell ) 3921 { 3922 if (!m_TableOpList.empty()) 3923 { 3924 ScInterpreterTableOpParams *const p = m_TableOpList.back(); 3925 if ( p->bCollectNotifications ) 3926 { 3927 if ( p->bRefresh ) 3928 { // refresh pointers only 3929 p->aNotifiedFormulaCells.push_back( pCell ); 3930 } 3931 else 3932 { // init both, address and pointer 3933 p->aNotifiedFormulaCells.push_back( pCell ); 3934 p->aNotifiedFormulaPos.push_back( pCell->aPos ); 3935 } 3936 } 3937 } 3938 } 3939 3940 void ScDocument::CalcAll() 3941 { 3942 PrepareFormulaCalc(); 3943 ClearLookupCaches(); // Ensure we don't deliver zombie data. 3944 sc::AutoCalcSwitch aSwitch(*this, true); 3945 for (const auto& a : maTabs) 3946 { 3947 if (a) 3948 a->SetDirtyVar(); 3949 } 3950 for (const auto& a : maTabs) 3951 { 3952 if (a) 3953 a->CalcAll(); 3954 } 3955 ClearFormulaTree(); 3956 3957 // In eternal hard recalc state caches were not added as listeners, 3958 // invalidate them so the next non-CalcAll() normal lookup will not be 3959 // presented with outdated data. 3960 if (GetHardRecalcState() == HardRecalcState::ETERNAL) 3961 ClearLookupCaches(); 3962 } 3963 3964 void ScDocument::CompileAll() 3965 { 3966 sc::CompileFormulaContext aCxt(this); 3967 for (const auto& a : maTabs) 3968 { 3969 if (a) 3970 a->CompileAll(aCxt); 3971 } 3972 3973 sc::SetFormulaDirtyContext aFormulaDirtyCxt; 3974 SetAllFormulasDirty(aFormulaDirtyCxt); 3975 } 3976 3977 void ScDocument::CompileXML() 3978 { 3979 bool bOldAutoCalc = GetAutoCalc(); 3980 SetAutoCalc( false ); 3981 ScProgress aProgress( GetDocumentShell(), ScResId( 3982 STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount(), true ); 3983 3984 sc::CompileFormulaContext aCxt(this); 3985 3986 // set AutoNameCache to speed up automatic name lookup 3987 OSL_ENSURE( !pAutoNameCache, "AutoNameCache already set" ); 3988 pAutoNameCache.reset( new ScAutoNameCache( this ) ); 3989 3990 if (pRangeName) 3991 pRangeName->CompileUnresolvedXML(aCxt); 3992 3993 std::for_each(maTabs.begin(), maTabs.end(), 3994 [&](ScTableUniquePtr & pTab) 3995 { 3996 if (pTab) 3997 pTab->CompileXML(aCxt, aProgress); 3998 } 3999 ); 4000 StartAllListeners(); 4001 4002 pAutoNameCache.reset(); // valid only during CompileXML, where cell contents don't change 4003 4004 if ( pValidationList ) 4005 { 4006 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 4007 pValidationList->CompileXML(); 4008 } 4009 4010 // Track all formula cells that were appended to the FormulaTrack during 4011 // import or CompileXML(). 4012 TrackFormulas(); 4013 4014 SetAutoCalc( bOldAutoCalc ); 4015 } 4016 4017 bool ScDocument::CompileErrorCells(FormulaError nErrCode) 4018 { 4019 bool bCompiled = false; 4020 sc::CompileFormulaContext aCxt(this); 4021 for (const auto& a : maTabs) 4022 { 4023 if (!a) 4024 continue; 4025 4026 if (a->CompileErrorCells(aCxt, nErrCode)) 4027 bCompiled = true; 4028 } 4029 4030 return bCompiled; 4031 } 4032 4033 void ScDocument::CalcAfterLoad( bool bStartListening ) 4034 { 4035 if (bIsClip) // Excel data is loaded from the Clipboard to a Clip-Doc 4036 return; // the calculation is then only performed when inserting into the real document 4037 4038 bCalcingAfterLoad = true; 4039 sc::CompileFormulaContext aCxt(this); 4040 { 4041 for (const auto& a : maTabs) 4042 { 4043 if (a) 4044 a->CalcAfterLoad(aCxt, bStartListening); 4045 } 4046 for (const auto& a : maTabs) 4047 { 4048 if (a) 4049 a->SetDirtyAfterLoad(); 4050 } 4051 } 4052 bCalcingAfterLoad = false; 4053 4054 SetDetectiveDirty(false); // No real changes yet 4055 4056 // #i112436# If formula cells are already dirty, they don't broadcast further changes. 4057 // So the source ranges of charts must be interpreted even if they are not visible, 4058 // similar to ScMyShapeResizer::CreateChartListener for loading own files (i104899). 4059 if (pChartListenerCollection) 4060 { 4061 const ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners(); 4062 for (auto const& it : rListeners) 4063 { 4064 const ScChartListener *const p = it.second.get(); 4065 InterpretDirtyCells(*p->GetRangeList()); 4066 } 4067 } 4068 } 4069 4070 FormulaError ScDocument::GetErrCode( const ScAddress& rPos ) const 4071 { 4072 SCTAB nTab = rPos.Tab(); 4073 if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4074 return maTabs[nTab]->GetErrCode( rPos ); 4075 return FormulaError::NONE; 4076 } 4077 4078 void ScDocument::ResetChanged( const ScRange& rRange ) 4079 { 4080 SCTAB nTabSize = static_cast<SCTAB>(maTabs.size()); 4081 SCTAB nTab1 = rRange.aStart.Tab(); 4082 SCTAB nTab2 = rRange.aEnd.Tab(); 4083 for (SCTAB nTab = nTab1; nTab1 <= nTab2 && nTab < nTabSize; ++nTab) 4084 if (maTabs[nTab]) 4085 maTabs[nTab]->ResetChanged(rRange); 4086 } 4087 4088 // Column widths / Row heights -------------------------------------- 4089 4090 void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth ) 4091 { 4092 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4093 maTabs[nTab]->SetColWidth( nCol, nNewWidth ); 4094 } 4095 4096 void ScDocument::SetColWidthOnly( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth ) 4097 { 4098 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4099 maTabs[nTab]->SetColWidthOnly( nCol, nNewWidth ); 4100 } 4101 4102 void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, sal_uInt16 nNewHeight ) 4103 { 4104 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4105 maTabs[nTab]->SetRowHeight( nRow, nNewHeight ); 4106 } 4107 4108 void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight ) 4109 { 4110 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4111 maTabs[nTab]->SetRowHeightRange 4112 ( nStartRow, nEndRow, nNewHeight, 1.0 ); 4113 } 4114 4115 void ScDocument::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight ) 4116 { 4117 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4118 maTabs[nTab]->SetRowHeightOnly( nStartRow, nEndRow, nNewHeight ); 4119 } 4120 4121 void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bManual ) 4122 { 4123 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4124 maTabs[nTab]->SetManualHeight( nStartRow, nEndRow, bManual ); 4125 } 4126 4127 sal_uInt16 ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const 4128 { 4129 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4130 return maTabs[nTab]->GetColWidth( nCol, bHiddenAsZero ); 4131 OSL_FAIL("wrong table number"); 4132 return 0; 4133 } 4134 4135 sal_uLong ScDocument::GetColWidth( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab ) const 4136 { 4137 const ScTable* pTab = FetchTable(nTab); 4138 if (!pTab) 4139 return 0; 4140 4141 return pTab->GetColWidth(nStartCol, nEndCol); 4142 } 4143 4144 sal_uInt16 ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const 4145 { 4146 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4147 return maTabs[nTab]->GetOriginalWidth( nCol ); 4148 OSL_FAIL("wrong table number"); 4149 return 0; 4150 } 4151 4152 sal_uInt16 ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const 4153 { 4154 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4155 return maTabs[nTab]->GetCommonWidth( nEndCol ); 4156 OSL_FAIL("Wrong table number"); 4157 return 0; 4158 } 4159 4160 sal_uInt16 ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const 4161 { 4162 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4163 return maTabs[nTab]->GetOriginalHeight( nRow ); 4164 OSL_FAIL("Wrong table number"); 4165 return 0; 4166 } 4167 4168 sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const 4169 { 4170 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4171 return maTabs[nTab]->GetRowHeight( nRow, nullptr, nullptr, bHiddenAsZero ); 4172 OSL_FAIL("Wrong sheet number"); 4173 return 0; 4174 } 4175 4176 sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, SCROW* pStartRow, SCROW* pEndRow ) const 4177 { 4178 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4179 return maTabs[nTab]->GetRowHeight( nRow, pStartRow, pEndRow ); 4180 OSL_FAIL("Wrong sheet number"); 4181 return 0; 4182 } 4183 4184 sal_uLong ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const 4185 { 4186 if (nStartRow == nEndRow) 4187 return GetRowHeight( nStartRow, nTab, bHiddenAsZero ); // faster for a single row 4188 4189 // check bounds because this method replaces former for(i=start;i<=end;++i) loops 4190 if (nStartRow > nEndRow) 4191 return 0; 4192 4193 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4194 return maTabs[nTab]->GetRowHeight( nStartRow, nEndRow, bHiddenAsZero ); 4195 4196 OSL_FAIL("wrong sheet number"); 4197 return 0; 4198 } 4199 4200 SCROW ScDocument::GetRowForHeight( SCTAB nTab, sal_uLong nHeight ) const 4201 { 4202 return maTabs[nTab]->GetRowForHeight(nHeight); 4203 } 4204 4205 sal_uLong ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, 4206 SCTAB nTab, double fScale ) const 4207 { 4208 // faster for a single row 4209 if (nStartRow == nEndRow) 4210 return static_cast<sal_uLong>(GetRowHeight( nStartRow, nTab) * fScale); 4211 4212 // check bounds because this method replaces former for(i=start;i<=end;++i) loops 4213 if (nStartRow > nEndRow) 4214 return 0; 4215 4216 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4217 return maTabs[nTab]->GetScaledRowHeight( nStartRow, nEndRow, fScale); 4218 4219 OSL_FAIL("wrong sheet number"); 4220 return 0; 4221 } 4222 4223 SCROW ScDocument::GetHiddenRowCount( SCROW nRow, SCTAB nTab ) const 4224 { 4225 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4226 return maTabs[nTab]->GetHiddenRowCount( nRow ); 4227 OSL_FAIL("wrong table number"); 4228 return 0; 4229 } 4230 4231 sal_uLong ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const 4232 { 4233 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4234 return maTabs[nTab]->GetColOffset( nCol, bHiddenAsZero ); 4235 OSL_FAIL("wrong table number"); 4236 return 0; 4237 } 4238 4239 sal_uLong ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const 4240 { 4241 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4242 return maTabs[nTab]->GetRowOffset( nRow, bHiddenAsZero ); 4243 OSL_FAIL("wrong table number"); 4244 return 0; 4245 } 4246 4247 sal_uInt16 ScDocument::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, OutputDevice* pDev, 4248 double nPPTX, double nPPTY, 4249 const Fraction& rZoomX, const Fraction& rZoomY, 4250 bool bFormula, const ScMarkData* pMarkData, 4251 const ScColWidthParam* pParam ) 4252 { 4253 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4254 return maTabs[nTab]->GetOptimalColWidth( nCol, pDev, nPPTX, nPPTY, 4255 rZoomX, rZoomY, bFormula, pMarkData, pParam ); 4256 OSL_FAIL("wrong table number"); 4257 return 0; 4258 } 4259 4260 long ScDocument::GetNeededSize( SCCOL nCol, SCROW nRow, SCTAB nTab, 4261 OutputDevice* pDev, 4262 double nPPTX, double nPPTY, 4263 const Fraction& rZoomX, const Fraction& rZoomY, 4264 bool bWidth, bool bTotalSize ) 4265 { 4266 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4267 return maTabs[nTab]->GetNeededSize 4268 ( nCol, nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, bTotalSize ); 4269 OSL_FAIL("wrong table number"); 4270 return 0; 4271 } 4272 4273 bool ScDocument::SetOptimalHeight( sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) 4274 { 4275 ScTable* pTab = FetchTable(nTab); 4276 if (!pTab) 4277 return false; 4278 4279 return pTab->SetOptimalHeight(rCxt, nStartRow, nEndRow); 4280 } 4281 4282 void ScDocument::UpdateAllRowHeights( sc::RowHeightContext& rCxt, const ScMarkData* pTabMark ) 4283 { 4284 // one progress across all (selected) sheets 4285 4286 sal_uLong nCellCount = 0; 4287 for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ ) 4288 if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) ) 4289 nCellCount += maTabs[nTab]->GetWeightedCount(); 4290 4291 ScProgress aProgress( GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true ); 4292 4293 sal_uLong nProgressStart = 0; 4294 for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ ) 4295 if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) ) 4296 { 4297 maTabs[nTab]->SetOptimalHeightOnly(rCxt, 0, MAXROW, &aProgress, nProgressStart); 4298 maTabs[nTab]->SetDrawPageSize(); 4299 nProgressStart += maTabs[nTab]->GetWeightedCount(); 4300 } 4301 } 4302 4303 // Column/Row - Flags ---------------------------------------------- 4304 4305 void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, bool bShow) 4306 { 4307 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4308 maTabs[nTab]->ShowCol( nCol, bShow ); 4309 } 4310 4311 void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, bool bShow) 4312 { 4313 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4314 maTabs[nTab]->ShowRow( nRow, bShow ); 4315 } 4316 4317 void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, bool bShow) 4318 { 4319 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4320 maTabs[nTab]->ShowRows( nRow1, nRow2, bShow ); 4321 } 4322 4323 void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, CRFlags nNewFlags ) 4324 { 4325 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4326 maTabs[nTab]->SetRowFlags( nRow, nNewFlags ); 4327 } 4328 4329 void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, CRFlags nNewFlags ) 4330 { 4331 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4332 maTabs[nTab]->SetRowFlags( nStartRow, nEndRow, nNewFlags ); 4333 } 4334 4335 CRFlags ScDocument::GetColFlags( SCCOL nCol, SCTAB nTab ) const 4336 { 4337 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4338 return maTabs[nTab]->GetColFlags( nCol ); 4339 OSL_FAIL("wrong table number"); 4340 return CRFlags::NONE; 4341 } 4342 4343 CRFlags ScDocument::GetRowFlags( SCROW nRow, SCTAB nTab ) const 4344 { 4345 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4346 return maTabs[nTab]->GetRowFlags( nRow ); 4347 OSL_FAIL("wrong table number"); 4348 return CRFlags::NONE; 4349 } 4350 4351 void ScDocument::GetAllRowBreaks(set<SCROW>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const 4352 { 4353 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4354 return; 4355 maTabs[nTab]->GetAllRowBreaks(rBreaks, bPage, bManual); 4356 } 4357 4358 void ScDocument::GetAllColBreaks(set<SCCOL>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const 4359 { 4360 if (!ValidTab(nTab) || !maTabs[nTab]) 4361 return; 4362 4363 maTabs[nTab]->GetAllColBreaks(rBreaks, bPage, bManual); 4364 } 4365 4366 ScBreakType ScDocument::HasRowBreak(SCROW nRow, SCTAB nTab) const 4367 { 4368 ScBreakType nType = ScBreakType::NONE; 4369 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow)) 4370 return nType; 4371 4372 if (maTabs[nTab]->HasRowPageBreak(nRow)) 4373 nType |= ScBreakType::Page; 4374 4375 if (maTabs[nTab]->HasRowManualBreak(nRow)) 4376 nType |= ScBreakType::Manual; 4377 4378 return nType; 4379 } 4380 4381 ScBreakType ScDocument::HasColBreak(SCCOL nCol, SCTAB nTab) const 4382 { 4383 ScBreakType nType = ScBreakType::NONE; 4384 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol)) 4385 return nType; 4386 4387 if (maTabs[nTab]->HasColPageBreak(nCol)) 4388 nType |= ScBreakType::Page; 4389 4390 if (maTabs[nTab]->HasColManualBreak(nCol)) 4391 nType |= ScBreakType::Manual; 4392 4393 return nType; 4394 } 4395 4396 void ScDocument::SetRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual) 4397 { 4398 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow)) 4399 return; 4400 4401 maTabs[nTab]->SetRowBreak(nRow, bPage, bManual); 4402 } 4403 4404 void ScDocument::SetColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual) 4405 { 4406 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol)) 4407 return; 4408 4409 maTabs[nTab]->SetColBreak(nCol, bPage, bManual); 4410 } 4411 4412 void ScDocument::RemoveRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual) 4413 { 4414 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow)) 4415 return; 4416 4417 maTabs[nTab]->RemoveRowBreak(nRow, bPage, bManual); 4418 } 4419 4420 void ScDocument::RemoveColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual) 4421 { 4422 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol)) 4423 return; 4424 4425 maTabs[nTab]->RemoveColBreak(nCol, bPage, bManual); 4426 } 4427 4428 Sequence<TablePageBreakData> ScDocument::GetRowBreakData(SCTAB nTab) const 4429 { 4430 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4431 return Sequence<TablePageBreakData>(); 4432 4433 return maTabs[nTab]->GetRowBreakData(); 4434 } 4435 4436 bool ScDocument::RowHidden(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const 4437 { 4438 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4439 return false; 4440 4441 return maTabs[nTab]->RowHidden(nRow, pFirstRow, pLastRow); 4442 } 4443 4444 bool ScDocument::HasHiddenRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4445 { 4446 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4447 return false; 4448 4449 return maTabs[nTab]->HasHiddenRows(nStartRow, nEndRow); 4450 } 4451 4452 bool ScDocument::ColHidden(SCCOL nCol, SCTAB nTab, SCCOL* pFirstCol, SCCOL* pLastCol) const 4453 { 4454 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4455 { 4456 if (pFirstCol) 4457 *pFirstCol = nCol; 4458 if (pLastCol) 4459 *pLastCol = nCol; 4460 return false; 4461 } 4462 4463 return maTabs[nTab]->ColHidden(nCol, pFirstCol, pLastCol); 4464 } 4465 4466 void ScDocument::SetRowHidden(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHidden) 4467 { 4468 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4469 return; 4470 4471 maTabs[nTab]->SetRowHidden(nStartRow, nEndRow, bHidden); 4472 } 4473 4474 void ScDocument::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bHidden) 4475 { 4476 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4477 return; 4478 4479 maTabs[nTab]->SetColHidden(nStartCol, nEndCol, bHidden); 4480 } 4481 4482 SCROW ScDocument::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4483 { 4484 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4485 return ::std::numeric_limits<SCROW>::max(); 4486 4487 return maTabs[nTab]->FirstVisibleRow(nStartRow, nEndRow); 4488 } 4489 4490 SCROW ScDocument::LastVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4491 { 4492 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4493 return ::std::numeric_limits<SCROW>::max(); 4494 4495 return maTabs[nTab]->LastVisibleRow(nStartRow, nEndRow); 4496 } 4497 4498 SCROW ScDocument::CountVisibleRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4499 { 4500 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4501 return 0; 4502 4503 return maTabs[nTab]->CountVisibleRows(nStartRow, nEndRow); 4504 } 4505 4506 bool ScDocument::RowFiltered(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const 4507 { 4508 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4509 return false; 4510 4511 return maTabs[nTab]->RowFiltered(nRow, pFirstRow, pLastRow); 4512 } 4513 4514 bool ScDocument::HasFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4515 { 4516 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4517 return false; 4518 4519 return maTabs[nTab]->HasFilteredRows(nStartRow, nEndRow); 4520 } 4521 4522 bool ScDocument::ColFiltered(SCCOL nCol, SCTAB nTab) const 4523 { 4524 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4525 return false; 4526 4527 return maTabs[nTab]->ColFiltered(nCol); 4528 } 4529 4530 void ScDocument::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bFiltered) 4531 { 4532 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4533 return; 4534 4535 maTabs[nTab]->SetRowFiltered(nStartRow, nEndRow, bFiltered); 4536 } 4537 4538 SCROW ScDocument::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4539 { 4540 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4541 return ::std::numeric_limits<SCROW>::max(); 4542 4543 return maTabs[nTab]->FirstNonFilteredRow(nStartRow, nEndRow); 4544 } 4545 4546 SCROW ScDocument::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4547 { 4548 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4549 return ::std::numeric_limits<SCROW>::max(); 4550 4551 return maTabs[nTab]->LastNonFilteredRow(nStartRow, nEndRow); 4552 } 4553 4554 SCROW ScDocument::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const 4555 { 4556 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4557 return 0; 4558 4559 return maTabs[nTab]->CountNonFilteredRows(nStartRow, nEndRow); 4560 } 4561 4562 bool ScDocument::IsManualRowHeight(SCROW nRow, SCTAB nTab) const 4563 { 4564 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4565 return false; 4566 4567 return maTabs[nTab]->IsManualRowHeight(nRow); 4568 } 4569 4570 void ScDocument::SyncColRowFlags() 4571 { 4572 for (const auto& a : maTabs) 4573 { 4574 if (a) 4575 a->SyncColRowFlags(); 4576 } 4577 } 4578 4579 SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const 4580 { 4581 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4582 return maTabs[nTab]->GetLastFlaggedRow(); 4583 return 0; 4584 } 4585 4586 SCCOL ScDocument::GetLastChangedCol( SCTAB nTab ) const 4587 { 4588 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4589 return maTabs[nTab]->GetLastChangedCol(); 4590 return 0; 4591 } 4592 4593 SCROW ScDocument::GetLastChangedRow( SCTAB nTab ) const 4594 { 4595 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4596 return maTabs[nTab]->GetLastChangedRow(); 4597 return 0; 4598 } 4599 4600 SCCOL ScDocument::GetNextDifferentChangedCol( SCTAB nTab, SCCOL nStart) const 4601 { 4602 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4603 { 4604 CRFlags nStartFlags = maTabs[nTab]->GetColFlags(nStart); 4605 sal_uInt16 nStartWidth = maTabs[nTab]->GetOriginalWidth(nStart); 4606 for (SCCOL nCol : maTabs[nTab]->GetColumnsRange( nStart + 1, MAXCOL)) 4607 { 4608 if (((nStartFlags & CRFlags::ManualBreak) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::ManualBreak)) || 4609 (nStartWidth != maTabs[nTab]->GetOriginalWidth(nCol)) || 4610 ((nStartFlags & CRFlags::Hidden) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::Hidden)) ) 4611 return nCol; 4612 } 4613 return MAXCOL+1; 4614 } 4615 return 0; 4616 } 4617 4618 SCROW ScDocument::GetNextDifferentChangedRow( SCTAB nTab, SCROW nStart) const 4619 { 4620 if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab]) 4621 return 0; 4622 4623 const ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlagsArray = maTabs[nTab]->GetRowFlagsArray(); 4624 if (!pRowFlagsArray) 4625 return 0; 4626 4627 if (!maTabs[nTab]->mpRowHeights || !maTabs[nTab]->mpHiddenRows) 4628 return 0; 4629 4630 size_t nIndex; // ignored 4631 SCROW nFlagsEndRow; 4632 SCROW nHiddenEndRow; 4633 SCROW nHeightEndRow; 4634 CRFlags nFlags; 4635 bool bHidden; 4636 sal_uInt16 nHeight; 4637 CRFlags nStartFlags = nFlags = pRowFlagsArray->GetValue( nStart, nIndex, nFlagsEndRow); 4638 bool bStartHidden = bHidden = maTabs[nTab]->RowHidden( nStart, nullptr, &nHiddenEndRow); 4639 sal_uInt16 nStartHeight = nHeight = maTabs[nTab]->GetRowHeight( nStart, nullptr, &nHeightEndRow, false); 4640 SCROW nRow; 4641 while ((nRow = std::min( nHiddenEndRow, std::min( nFlagsEndRow, nHeightEndRow)) + 1) <= MAXROW) 4642 { 4643 if (nFlagsEndRow < nRow) 4644 nFlags = pRowFlagsArray->GetValue( nRow, nIndex, nFlagsEndRow); 4645 if (nHiddenEndRow < nRow) 4646 bHidden = maTabs[nTab]->RowHidden( nRow, nullptr, &nHiddenEndRow); 4647 if (nHeightEndRow < nRow) 4648 nHeight = maTabs[nTab]->GetRowHeight( nRow, nullptr, &nHeightEndRow, false); 4649 4650 if (((nStartFlags & CRFlags::ManualBreak) != (nFlags & CRFlags::ManualBreak)) || 4651 ((nStartFlags & CRFlags::ManualSize) != (nFlags & CRFlags::ManualSize)) || 4652 (bStartHidden != bHidden) || 4653 (nStartHeight != nHeight)) 4654 return nRow; 4655 } 4656 4657 return MAXROW+1; 4658 } 4659 4660 void ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault) 4661 { 4662 nDefault = 0; 4663 ScDocAttrIterator aDocAttrItr(this, nTab, nCol, 0, nCol, nLastRow); 4664 SCCOL nColumn; 4665 SCROW nStartRow; 4666 SCROW nEndRow; 4667 const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow); 4668 if (nEndRow < nLastRow) 4669 { 4670 ScDefaultAttrSet aSet; 4671 ScDefaultAttrSet::iterator aItr = aSet.end(); 4672 while (pAttr) 4673 { 4674 ScDefaultAttr aAttr(pAttr); 4675 aItr = aSet.find(aAttr); 4676 if (aItr == aSet.end()) 4677 { 4678 aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1); 4679 aAttr.nFirst = nStartRow; 4680 aSet.insert(aAttr); 4681 } 4682 else 4683 { 4684 aAttr.nCount = aItr->nCount + static_cast<SCSIZE>(nEndRow - nStartRow + 1); 4685 aAttr.nFirst = aItr->nFirst; 4686 aSet.erase(aItr); 4687 aSet.insert(aAttr); 4688 } 4689 pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow); 4690 } 4691 ScDefaultAttrSet::iterator aDefaultItr = aSet.begin(); 4692 aItr = aDefaultItr; 4693 ++aItr; 4694 while (aItr != aSet.end()) 4695 { 4696 // for entries with equal count, use the one with the lowest start row, 4697 // don't use the random order of pointer comparisons 4698 if ( aItr->nCount > aDefaultItr->nCount || 4699 ( aItr->nCount == aDefaultItr->nCount && aItr->nFirst < aDefaultItr->nFirst ) ) 4700 aDefaultItr = aItr; 4701 ++aItr; 4702 } 4703 nDefault = aDefaultItr->nFirst; 4704 } 4705 } 4706 4707 void ScDocument::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab ) 4708 { 4709 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4710 maTabs[nTab]->StripHidden( rX1, rY1, rX2, rY2 ); 4711 } 4712 4713 void ScDocument::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab ) 4714 { 4715 if ( ValidTab(nTab) && maTabs[nTab] ) 4716 maTabs[nTab]->ExtendHidden( rX1, rY1, rX2, rY2 ); 4717 } 4718 4719 // Attribute ---------------------------------------------------------- 4720 4721 const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const 4722 { 4723 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && 4724 nCol < maTabs[nTab]->GetAllocatedColumnsCount()) 4725 { 4726 const SfxPoolItem* pTemp = maTabs[nTab]->GetAttr( nCol, nRow, nWhich ); 4727 if (pTemp) 4728 return pTemp; 4729 else 4730 { 4731 OSL_FAIL( "Attribute Null" ); 4732 } 4733 } 4734 return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich ); 4735 } 4736 4737 const SfxPoolItem* ScDocument::GetAttr( const ScAddress& rPos, sal_uInt16 nWhich ) const 4738 { 4739 return GetAttr(rPos.Col(), rPos.Row(), rPos.Tab(), nWhich); 4740 } 4741 4742 const ScPatternAttr* ScDocument::GetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 4743 { 4744 if (TableExists(nTab)) 4745 return maTabs[nTab]->GetPattern( nCol, nRow ); 4746 return nullptr; 4747 } 4748 4749 const ScPatternAttr* ScDocument::GetPattern( const ScAddress& rPos ) const 4750 { 4751 if (TableExists(rPos.Tab())) 4752 return maTabs[rPos.Tab()]->GetPattern(rPos.Col(), rPos.Row()); 4753 4754 return nullptr; 4755 } 4756 4757 const ScPatternAttr* ScDocument::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const 4758 { 4759 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4760 return maTabs[nTab]->GetMostUsedPattern( nCol, nStartRow, nEndRow ); 4761 return nullptr; 4762 } 4763 4764 void ScDocument::ApplyAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem& rAttr ) 4765 { 4766 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4767 maTabs[nTab]->ApplyAttr( nCol, nRow, rAttr ); 4768 } 4769 4770 void ScDocument::ApplyPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr ) 4771 { 4772 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4773 maTabs[nTab]->ApplyPattern( nCol, nRow, rAttr ); 4774 } 4775 4776 void ScDocument::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, 4777 SCCOL nEndCol, SCROW nEndRow, 4778 const ScMarkData& rMark, 4779 const ScPatternAttr& rAttr, 4780 ScEditDataArray* pDataArray, 4781 bool* const pIsChanged ) 4782 { 4783 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4784 for (const auto& rTab : rMark) 4785 { 4786 if (rTab >= nMax) 4787 break; 4788 if (maTabs[rTab]) 4789 maTabs[rTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr, pDataArray, pIsChanged ); 4790 } 4791 } 4792 4793 void ScDocument::ApplyPatternAreaTab( SCCOL nStartCol, SCROW nStartRow, 4794 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScPatternAttr& rAttr ) 4795 { 4796 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 4797 if (maTabs[nTab]) 4798 maTabs[nTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr ); 4799 } 4800 4801 void ScDocument::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange, 4802 const ScMarkData& rMark, const ScPatternAttr& rPattern, SvNumFormatType nNewType ) 4803 { 4804 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4805 for (const auto& rTab : rMark) 4806 { 4807 if (rTab >= nMax) 4808 break; 4809 if (maTabs[rTab]) 4810 maTabs[rTab]->ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType ); 4811 } 4812 } 4813 4814 void ScDocument::AddCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex ) 4815 { 4816 if(static_cast<size_t>(nTab) >= maTabs.size()) 4817 return; 4818 4819 if(!maTabs[nTab]) 4820 return; 4821 4822 maTabs[nTab]->AddCondFormatData(rRange, nIndex); 4823 } 4824 4825 void ScDocument::RemoveCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex ) 4826 { 4827 if(static_cast<size_t>(nTab) >= maTabs.size()) 4828 return; 4829 4830 if(!maTabs[nTab]) 4831 return; 4832 4833 maTabs[nTab]->RemoveCondFormatData(rRange, nIndex); 4834 } 4835 4836 void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle) 4837 { 4838 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 4839 if (maTabs[nTab]) 4840 maTabs[nTab]->ApplyStyle( nCol, nRow, &rStyle ); 4841 } 4842 4843 void ScDocument::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, 4844 SCCOL nEndCol, SCROW nEndRow, 4845 const ScMarkData& rMark, 4846 const ScStyleSheet& rStyle) 4847 { 4848 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4849 for (const auto& rTab : rMark) 4850 { 4851 if (rTab >= nMax) 4852 break; 4853 if (maTabs[rTab]) 4854 maTabs[rTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle ); 4855 } 4856 } 4857 4858 void ScDocument::ApplyStyleAreaTab( SCCOL nStartCol, SCROW nStartRow, 4859 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScStyleSheet& rStyle) 4860 { 4861 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 4862 if (maTabs[nTab]) 4863 maTabs[nTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle ); 4864 } 4865 4866 void ScDocument::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark) 4867 { 4868 // ApplySelectionStyle needs multi mark 4869 if ( rMark.IsMarked() && !rMark.IsMultiMarked() ) 4870 { 4871 ScRange aRange; 4872 rMark.GetMarkArea( aRange ); 4873 ApplyStyleArea( aRange.aStart.Col(), aRange.aStart.Row(), 4874 aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rStyle ); 4875 } 4876 else 4877 { 4878 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4879 for (const auto& rTab : rMark) 4880 { 4881 if (rTab >= nMax) 4882 break; 4883 if ( maTabs[rTab] ) 4884 maTabs[rTab]->ApplySelectionStyle( rStyle, rMark ); 4885 } 4886 } 4887 } 4888 4889 void ScDocument::ApplySelectionLineStyle( const ScMarkData& rMark, 4890 const SvxBorderLine* pLine, bool bColorOnly ) 4891 { 4892 if ( bColorOnly && !pLine ) 4893 return; 4894 4895 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4896 for (const auto& rTab : rMark) 4897 { 4898 if (rTab >= nMax) 4899 break; 4900 if (maTabs[rTab]) 4901 maTabs[rTab]->ApplySelectionLineStyle( rMark, pLine, bColorOnly ); 4902 } 4903 } 4904 4905 const ScStyleSheet* ScDocument::GetStyle( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 4906 { 4907 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 4908 return maTabs[nTab]->GetStyle(nCol, nRow); 4909 else 4910 return nullptr; 4911 } 4912 4913 const ScStyleSheet* ScDocument::GetSelectionStyle( const ScMarkData& rMark ) const 4914 { 4915 bool bEqual = true; 4916 bool bFound; 4917 4918 const ScStyleSheet* pStyle = nullptr; 4919 const ScStyleSheet* pNewStyle; 4920 4921 if ( rMark.IsMultiMarked() ) 4922 { 4923 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 4924 for (const auto& rTab : rMark) 4925 { 4926 if (rTab >= nMax) 4927 break; 4928 4929 if (maTabs[rTab]) 4930 { 4931 pNewStyle = maTabs[rTab]->GetSelectionStyle( rMark, bFound ); 4932 if (bFound) 4933 { 4934 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) 4935 bEqual = false; // different 4936 pStyle = pNewStyle; 4937 } 4938 } 4939 } 4940 } 4941 if ( rMark.IsMarked() ) 4942 { 4943 ScRange aRange; 4944 rMark.GetMarkArea( aRange ); 4945 for (SCTAB i=aRange.aStart.Tab(); i<=aRange.aEnd.Tab() && bEqual && i < static_cast<SCTAB>(maTabs.size()); i++) 4946 if (maTabs[i] && rMark.GetTableSelect(i)) 4947 { 4948 pNewStyle = maTabs[i]->GetAreaStyle( bFound, 4949 aRange.aStart.Col(), aRange.aStart.Row(), 4950 aRange.aEnd.Col(), aRange.aEnd.Row() ); 4951 if (bFound) 4952 { 4953 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) 4954 bEqual = false; // different 4955 pStyle = pNewStyle; 4956 } 4957 } 4958 } 4959 4960 return bEqual ? pStyle : nullptr; 4961 } 4962 4963 void ScDocument::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved, 4964 OutputDevice* pDev, 4965 double nPPTX, double nPPTY, 4966 const Fraction& rZoomX, const Fraction& rZoomY ) 4967 { 4968 for (const auto& a : maTabs) 4969 { 4970 if (a) 4971 a->StyleSheetChanged 4972 ( pStyleSheet, bRemoved, pDev, nPPTX, nPPTY, rZoomX, rZoomY ); 4973 } 4974 4975 if ( pStyleSheet && pStyleSheet->GetName() == ScResId(STR_STYLENAME_STANDARD) ) 4976 { 4977 // update attributes for all note objects 4978 ScDetectiveFunc::UpdateAllComments( *this ); 4979 } 4980 } 4981 4982 bool ScDocument::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const 4983 { 4984 if ( bStyleSheetUsageInvalid || rStyle.GetUsage() == ScStyleSheet::Usage::UNKNOWN ) 4985 { 4986 SfxStyleSheetIterator aIter( mxPoolHelper->GetStylePool(), 4987 SfxStyleFamily::Para ); 4988 for ( const SfxStyleSheetBase* pStyle = aIter.First(); pStyle; 4989 pStyle = aIter.Next() ) 4990 { 4991 if (pStyle->isScStyleSheet()) 4992 { 4993 const ScStyleSheet* pScStyle = static_cast<const ScStyleSheet*>( pStyle ); 4994 pScStyle->SetUsage( ScStyleSheet::Usage::NOTUSED ); 4995 } 4996 } 4997 4998 bool bIsUsed = false; 4999 5000 for (const auto& a : maTabs) 5001 { 5002 if (a && a->IsStyleSheetUsed( rStyle ) ) 5003 bIsUsed = true; 5004 } 5005 5006 bStyleSheetUsageInvalid = false; 5007 5008 return bIsUsed; 5009 } 5010 5011 return rStyle.GetUsage() == ScStyleSheet::Usage::USED; 5012 } 5013 5014 bool ScDocument::ApplyFlagsTab( SCCOL nStartCol, SCROW nStartRow, 5015 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags ) 5016 { 5017 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 5018 if (maTabs[nTab]) 5019 return maTabs[nTab]->ApplyFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags ); 5020 5021 OSL_FAIL("ApplyFlags: wrong table"); 5022 return false; 5023 } 5024 5025 bool ScDocument::RemoveFlagsTab( SCCOL nStartCol, SCROW nStartRow, 5026 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags ) 5027 { 5028 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 5029 if (maTabs[nTab]) 5030 return maTabs[nTab]->RemoveFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags ); 5031 5032 OSL_FAIL("RemoveFlags: wrong table"); 5033 return false; 5034 } 5035 5036 void ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr ) 5037 { 5038 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 5039 if (maTabs[nTab]) 5040 maTabs[nTab]->SetPattern( nCol, nRow, rAttr ); 5041 } 5042 5043 void ScDocument::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr ) 5044 { 5045 SCTAB nTab = rPos.Tab(); 5046 if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 5047 maTabs[nTab]->SetPattern( rPos, rAttr ); 5048 } 5049 5050 std::unique_ptr<ScPatternAttr> ScDocument::CreateSelectionPattern( const ScMarkData& rMark, bool bDeep ) 5051 { 5052 ScMergePatternState aState; 5053 5054 if ( rMark.IsMultiMarked() ) // multi selection 5055 { 5056 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5057 for (const auto& rTab : rMark) 5058 { 5059 if (rTab >= nMax) 5060 break; 5061 if (maTabs[rTab]) 5062 maTabs[rTab]->MergeSelectionPattern( aState, rMark, bDeep ); 5063 } 5064 } 5065 if ( rMark.IsMarked() ) // single selection 5066 { 5067 ScRange aRange; 5068 rMark.GetMarkArea(aRange); 5069 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5070 for (const auto& rTab : rMark) 5071 { 5072 if (rTab >= nMax) 5073 break; 5074 if (maTabs[rTab]) 5075 maTabs[rTab]->MergePatternArea( aState, 5076 aRange.aStart.Col(), aRange.aStart.Row(), 5077 aRange.aEnd.Col(), aRange.aEnd.Row(), bDeep ); 5078 } 5079 } 5080 5081 OSL_ENSURE( aState.pItemSet, "SelectionPattern Null" ); 5082 if (aState.pItemSet) 5083 { 5084 std::unique_ptr<ScPatternAttr> pPattern(new ScPatternAttr( std::move(aState.pItemSet) )); 5085 if (aState.mbValidPatternId) 5086 pPattern->SetKey(aState.mnPatternId); 5087 5088 return pPattern; 5089 } 5090 else 5091 return std::unique_ptr<ScPatternAttr>(new ScPatternAttr( GetPool() )); // empty 5092 } 5093 5094 const ScPatternAttr* ScDocument::GetSelectionPattern( const ScMarkData& rMark ) 5095 { 5096 pSelectionAttr = CreateSelectionPattern( rMark ); 5097 return pSelectionAttr.get(); 5098 } 5099 5100 void ScDocument::GetSelectionFrame( const ScMarkData& rMark, 5101 SvxBoxItem& rLineOuter, 5102 SvxBoxInfoItem& rLineInner ) 5103 { 5104 rLineOuter.SetLine(nullptr, SvxBoxItemLine::TOP); 5105 rLineOuter.SetLine(nullptr, SvxBoxItemLine::BOTTOM); 5106 rLineOuter.SetLine(nullptr, SvxBoxItemLine::LEFT); 5107 rLineOuter.SetLine(nullptr, SvxBoxItemLine::RIGHT); 5108 rLineOuter.SetAllDistances(0); 5109 5110 rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::HORI); 5111 rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::VERT); 5112 rLineInner.SetTable(true); 5113 rLineInner.SetDist(true); 5114 rLineInner.SetMinDist(false); 5115 5116 ScLineFlags aFlags; 5117 5118 if( rMark.IsMultiMarked() ) 5119 { 5120 ScRangeList aRangeList; 5121 rMark.FillRangeListWithMarks( &aRangeList, false ); 5122 size_t nRangeCount = aRangeList.size(); 5123 bool bMultipleRows = false, bMultipleCols = false; 5124 for( size_t nRangeIdx = 0; nRangeIdx < nRangeCount; ++nRangeIdx ) 5125 { 5126 const ScRange & rRange = aRangeList[ nRangeIdx ]; 5127 bMultipleRows = ( bMultipleRows || ( rRange.aStart.Row() != rRange.aEnd.Row() ) ); 5128 bMultipleCols = ( bMultipleCols || ( rRange.aStart.Col() != rRange.aEnd.Col() ) ); 5129 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5130 for (const auto& rTab : rMark) 5131 { 5132 if (rTab >= nMax) 5133 break; 5134 5135 if (maTabs[rTab]) 5136 maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags, 5137 rRange.aStart.Col(), rRange.aStart.Row(), 5138 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5139 } 5140 } 5141 rLineInner.EnableHor( bMultipleRows ); 5142 rLineInner.EnableVer( bMultipleCols ); 5143 } 5144 else if( rMark.IsMarked() ) 5145 { 5146 ScRange aRange; 5147 rMark.GetMarkArea(aRange); 5148 rLineInner.EnableHor( aRange.aStart.Row() != aRange.aEnd.Row() ); 5149 rLineInner.EnableVer( aRange.aStart.Col() != aRange.aEnd.Col() ); 5150 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5151 for (const auto& rTab : rMark) 5152 { 5153 if (rTab >= nMax) 5154 break; 5155 5156 if (maTabs[rTab]) 5157 maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags, 5158 aRange.aStart.Col(), aRange.aStart.Row(), 5159 aRange.aEnd.Col(), aRange.aEnd.Row() ); 5160 } 5161 } 5162 5163 // Evaluate don't care Status 5164 5165 rLineInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, ( aFlags.nLeft != SC_LINE_DONTCARE ) ); 5166 rLineInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, ( aFlags.nRight != SC_LINE_DONTCARE ) ); 5167 rLineInner.SetValid( SvxBoxInfoItemValidFlags::TOP, ( aFlags.nTop != SC_LINE_DONTCARE ) ); 5168 rLineInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, ( aFlags.nBottom != SC_LINE_DONTCARE ) ); 5169 rLineInner.SetValid( SvxBoxInfoItemValidFlags::HORI, ( aFlags.nHori != SC_LINE_DONTCARE ) ); 5170 rLineInner.SetValid( SvxBoxInfoItemValidFlags::VERT, ( aFlags.nVert != SC_LINE_DONTCARE ) ); 5171 } 5172 5173 bool ScDocument::HasAttrib( SCCOL nCol1, SCROW nRow1, SCTAB nTab1, 5174 SCCOL nCol2, SCROW nRow2, SCTAB nTab2, HasAttrFlags nMask ) const 5175 { 5176 if ( nMask & HasAttrFlags::Rotate ) 5177 { 5178 // Is attribute used in document? 5179 // (as in fillinfo) 5180 5181 ScDocumentPool* pPool = mxPoolHelper->GetDocPool(); 5182 5183 bool bAnyItem = false; 5184 for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_ROTATE_VALUE)) 5185 { 5186 // 90 or 270 degrees is former SvxOrientationItem - only look for other values 5187 // (see ScPatternAttr::GetCellOrientation) 5188 sal_Int32 nAngle = static_cast<const SfxInt32Item*>(pItem)->GetValue(); 5189 if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 ) 5190 { 5191 bAnyItem = true; 5192 break; 5193 } 5194 } 5195 if (!bAnyItem) 5196 nMask &= ~HasAttrFlags::Rotate; 5197 } 5198 5199 if (nMask == HasAttrFlags::NONE) 5200 return false; 5201 5202 bool bFound = false; 5203 for (SCTAB i=nTab1; i<=nTab2 && !bFound && i < static_cast<SCTAB>(maTabs.size()); i++) 5204 if (maTabs[i]) 5205 { 5206 if ( nMask & HasAttrFlags::RightOrCenter ) 5207 { 5208 // On a RTL sheet, don't start to look for the default left value 5209 // (which is then logically right), instead always assume true. 5210 // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets. 5211 5212 if ( IsLayoutRTL(i) ) 5213 bFound = true; 5214 } 5215 5216 if ( !bFound ) 5217 bFound = maTabs[i]->HasAttrib( nCol1, nRow1, nCol2, nRow2, nMask ); 5218 } 5219 5220 return bFound; 5221 } 5222 5223 bool ScDocument::HasAttrib( const ScRange& rRange, HasAttrFlags nMask ) const 5224 { 5225 return HasAttrib( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), 5226 rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), 5227 nMask ); 5228 } 5229 5230 void ScDocument::FindMaxRotCol( SCTAB nTab, RowInfo* pRowInfo, SCSIZE nArrCount, 5231 SCCOL nX1, SCCOL nX2 ) const 5232 { 5233 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 5234 maTabs[nTab]->FindMaxRotCol( pRowInfo, nArrCount, nX1, nX2 ); 5235 else 5236 { 5237 OSL_FAIL("FindMaxRotCol: wrong table"); 5238 } 5239 } 5240 5241 void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab, 5242 const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop, 5243 const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const 5244 { 5245 //TODO: consider page limits for printing !!!!! 5246 5247 const SvxBoxItem* pThisAttr = GetEffItem( nCol, nRow, nTab, ATTR_BORDER ); 5248 OSL_ENSURE(pThisAttr,"where is the attribute?"); 5249 5250 const SvxBorderLine* pLeftLine = pThisAttr->GetLeft(); 5251 const SvxBorderLine* pTopLine = pThisAttr->GetTop(); 5252 const SvxBorderLine* pRightLine = pThisAttr->GetRight(); 5253 const SvxBorderLine* pBottomLine = pThisAttr->GetBottom(); 5254 5255 if ( nCol > 0 ) 5256 { 5257 const SvxBorderLine* pOther = GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER )->GetRight(); 5258 if ( ScHasPriority( pOther, pLeftLine ) ) 5259 pLeftLine = pOther; 5260 } 5261 if ( nRow > 0 ) 5262 { 5263 const SvxBorderLine* pOther = GetEffItem( nCol, nRow-1, nTab, ATTR_BORDER )->GetBottom(); 5264 if ( ScHasPriority( pOther, pTopLine ) ) 5265 pTopLine = pOther; 5266 } 5267 if ( nCol < MAXCOL ) 5268 { 5269 const SvxBorderLine* pOther = GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER )->GetLeft(); 5270 if ( ScHasPriority( pOther, pRightLine ) ) 5271 pRightLine = pOther; 5272 } 5273 if ( nRow < MAXROW ) 5274 { 5275 const SvxBorderLine* pOther = GetEffItem( nCol, nRow+1, nTab, ATTR_BORDER )->GetTop(); 5276 if ( ScHasPriority( pOther, pBottomLine ) ) 5277 pBottomLine = pOther; 5278 } 5279 5280 if (ppLeft) 5281 *ppLeft = pLeftLine; 5282 if (ppTop) 5283 *ppTop = pTopLine; 5284 if (ppRight) 5285 *ppRight = pRightLine; 5286 if (ppBottom) 5287 *ppBottom = pBottomLine; 5288 } 5289 5290 bool ScDocument::IsBlockEmpty( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, 5291 SCCOL nEndCol, SCROW nEndRow, bool bIgnoreNotes ) const 5292 { 5293 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 5294 if (maTabs[nTab]) 5295 return maTabs[nTab]->IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow, bIgnoreNotes ); 5296 5297 OSL_FAIL("wrong table number"); 5298 return false; 5299 } 5300 5301 void ScDocument::LockTable(SCTAB nTab) 5302 { 5303 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 5304 maTabs[nTab]->LockTable(); 5305 else 5306 { 5307 OSL_FAIL("wrong table number"); 5308 } 5309 } 5310 5311 void ScDocument::UnlockTable(SCTAB nTab) 5312 { 5313 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 5314 maTabs[nTab]->UnlockTable(); 5315 else 5316 { 5317 OSL_FAIL("wrong table number"); 5318 } 5319 } 5320 5321 bool ScDocument::IsBlockEditable( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, 5322 SCCOL nEndCol, SCROW nEndRow, 5323 bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const 5324 { 5325 // import into read-only document is possible 5326 if (!bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly()) 5327 { 5328 if ( pOnlyNotBecauseOfMatrix ) 5329 *pOnlyNotBecauseOfMatrix = false; 5330 return false; 5331 } 5332 5333 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size())) 5334 if (maTabs[nTab]) 5335 return maTabs[nTab]->IsBlockEditable( nStartCol, nStartRow, nEndCol, 5336 nEndRow, pOnlyNotBecauseOfMatrix ); 5337 5338 OSL_FAIL("wrong table number"); 5339 if ( pOnlyNotBecauseOfMatrix ) 5340 *pOnlyNotBecauseOfMatrix = false; 5341 return false; 5342 } 5343 5344 bool ScDocument::IsSelectionEditable( const ScMarkData& rMark, 5345 bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const 5346 { 5347 // import into read-only document is possible 5348 if ( !bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly() ) 5349 { 5350 if ( pOnlyNotBecauseOfMatrix ) 5351 *pOnlyNotBecauseOfMatrix = false; 5352 return false; 5353 } 5354 5355 ScRange aRange; 5356 rMark.GetMarkArea(aRange); 5357 5358 bool bOk = true; 5359 bool bMatrix = ( pOnlyNotBecauseOfMatrix != nullptr ); 5360 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5361 for (const auto& rTab : rMark) 5362 { 5363 if (rTab >= nMax) 5364 break; 5365 5366 if ( maTabs[rTab] ) 5367 { 5368 if (rMark.IsMarked()) 5369 { 5370 if ( !maTabs[rTab]->IsBlockEditable( aRange.aStart.Col(), 5371 aRange.aStart.Row(), aRange.aEnd.Col(), 5372 aRange.aEnd.Row(), pOnlyNotBecauseOfMatrix ) ) 5373 { 5374 bOk = false; 5375 if ( pOnlyNotBecauseOfMatrix ) 5376 bMatrix = *pOnlyNotBecauseOfMatrix; 5377 } 5378 } 5379 if (rMark.IsMultiMarked()) 5380 { 5381 if ( !maTabs[rTab]->IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix ) ) 5382 { 5383 bOk = false; 5384 if ( pOnlyNotBecauseOfMatrix ) 5385 bMatrix = *pOnlyNotBecauseOfMatrix; 5386 } 5387 } 5388 } 5389 5390 if (!bOk && !bMatrix) 5391 break; 5392 } 5393 5394 if ( pOnlyNotBecauseOfMatrix ) 5395 *pOnlyNotBecauseOfMatrix = ( !bOk && bMatrix ); 5396 5397 return bOk; 5398 } 5399 5400 bool ScDocument::HasSelectedBlockMatrixFragment( SCCOL nStartCol, SCROW nStartRow, 5401 SCCOL nEndCol, SCROW nEndRow, 5402 const ScMarkData& rMark ) const 5403 { 5404 bool bOk = true; 5405 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5406 for (const auto& rTab : rMark) 5407 { 5408 if (rTab >= nMax) 5409 break; 5410 5411 if (maTabs[rTab] && maTabs[rTab]->HasBlockMatrixFragment( nStartCol, nStartRow, nEndCol, nEndRow )) 5412 bOk = false; 5413 5414 if (!bOk) 5415 break; 5416 } 5417 5418 return !bOk; 5419 } 5420 5421 bool ScDocument::GetMatrixFormulaRange( const ScAddress& rCellPos, ScRange& rMatrix ) 5422 { 5423 // if rCell is part of a matrix formula, return its complete range 5424 5425 ScFormulaCell* pFCell = GetFormulaCell(rCellPos); 5426 if (!pFCell) 5427 // not a formula cell. Bail out. 5428 return false; 5429 5430 ScAddress aOrigin = rCellPos; 5431 if (!pFCell->GetMatrixOrigin(aOrigin)) 5432 // Failed to get the address of the matrix origin. 5433 return false; 5434 5435 if (aOrigin != rCellPos) 5436 { 5437 pFCell = GetFormulaCell(aOrigin); 5438 if (!pFCell) 5439 // The matrix origin cell is not a formula cell !? Something is up... 5440 return false; 5441 } 5442 5443 SCCOL nSizeX; 5444 SCROW nSizeY; 5445 pFCell->GetMatColsRows(nSizeX, nSizeY); 5446 if (nSizeX <= 0 || nSizeY <= 0) 5447 { 5448 // GetMatrixEdge computes also dimensions of the matrix 5449 // if not already done (may occur if document is loaded 5450 // from old file format). 5451 // Needs an "invalid" initialized address. 5452 aOrigin.SetInvalid(); 5453 pFCell->GetMatrixEdge(aOrigin); 5454 pFCell->GetMatColsRows(nSizeX, nSizeY); 5455 } 5456 5457 if (nSizeX <= 0 || nSizeY <= 0) 5458 // Matrix size is still invalid. Give up. 5459 return false; 5460 5461 ScAddress aEnd( aOrigin.Col() + nSizeX - 1, 5462 aOrigin.Row() + nSizeY - 1, 5463 aOrigin.Tab() ); 5464 5465 rMatrix.aStart = aOrigin; 5466 rMatrix.aEnd = aEnd; 5467 5468 return true; 5469 } 5470 5471 void ScDocument::ExtendOverlapped( SCCOL& rStartCol, SCROW& rStartRow, 5472 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) const 5473 { 5474 if ( ValidColRow(rStartCol,rStartRow) && ValidColRow(nEndCol,nEndRow) && ValidTab(nTab) ) 5475 { 5476 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 5477 { 5478 SCCOL nCol; 5479 SCCOL nOldCol = rStartCol; 5480 SCROW nOldRow = rStartRow; 5481 for (nCol=nOldCol; nCol<=nEndCol; nCol++) 5482 while (GetAttr(nCol,rStartRow,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped()) 5483 --rStartRow; 5484 5485 //TODO: pass on ? 5486 5487 ScAttrArray* pAttrArray = maTabs[nTab]->aCol[nOldCol].pAttrArray.get(); 5488 SCSIZE nIndex; 5489 if ( pAttrArray->Count() ) 5490 pAttrArray->Search( nOldRow, nIndex ); 5491 else 5492 nIndex = 0; 5493 SCROW nAttrPos = nOldRow; 5494 while (nAttrPos<=nEndRow) 5495 { 5496 OSL_ENSURE( nIndex < pAttrArray->Count(), "Wrong index in AttrArray" ); 5497 5498 bool bHorOverlapped; 5499 if ( pAttrArray->Count() ) 5500 bHorOverlapped = pAttrArray->mvData[nIndex].pPattern->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped(); 5501 else 5502 bHorOverlapped = GetDefPattern()->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped(); 5503 if ( bHorOverlapped ) 5504 { 5505 SCROW nEndRowSeg = (pAttrArray->Count()) ? pAttrArray->mvData[nIndex].nEndRow : MAXROW; 5506 SCROW nLoopEndRow = std::min( nEndRow, nEndRowSeg ); 5507 for (SCROW nAttrRow = nAttrPos; nAttrRow <= nLoopEndRow; nAttrRow++) 5508 { 5509 SCCOL nTempCol = nOldCol; 5510 do 5511 --nTempCol; 5512 while (GetAttr(nTempCol,nAttrRow,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped()); 5513 if (nTempCol < rStartCol) 5514 rStartCol = nTempCol; 5515 } 5516 } 5517 if ( pAttrArray->Count() ) 5518 { 5519 nAttrPos = pAttrArray->mvData[nIndex].nEndRow + 1; 5520 ++nIndex; 5521 } 5522 else 5523 nAttrPos = MAXROW + 1; 5524 } 5525 } 5526 } 5527 else 5528 { 5529 OSL_FAIL("ExtendOverlapped: invalid range"); 5530 } 5531 } 5532 5533 void ScDocument::ExtendMergeSel( SCCOL nStartCol, SCROW nStartRow, 5534 SCCOL& rEndCol, SCROW& rEndRow, 5535 const ScMarkData& rMark, bool bRefresh ) 5536 { 5537 // use all selected sheets from rMark 5538 5539 SCCOL nOldEndCol = rEndCol; 5540 SCROW nOldEndRow = rEndRow; 5541 5542 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5543 for (const auto& rTab : rMark) 5544 { 5545 if (rTab >= nMax) 5546 break; 5547 5548 if ( maTabs[rTab] ) 5549 { 5550 SCCOL nThisEndCol = nOldEndCol; 5551 SCROW nThisEndRow = nOldEndRow; 5552 ExtendMerge( nStartCol, nStartRow, nThisEndCol, nThisEndRow, rTab, bRefresh ); 5553 if ( nThisEndCol > rEndCol ) 5554 rEndCol = nThisEndCol; 5555 if ( nThisEndRow > rEndRow ) 5556 rEndRow = nThisEndRow; 5557 } 5558 } 5559 } 5560 5561 bool ScDocument::ExtendMerge( SCCOL nStartCol, SCROW nStartRow, 5562 SCCOL& rEndCol, SCROW& rEndRow, 5563 SCTAB nTab, bool bRefresh ) 5564 { 5565 bool bFound = false; 5566 if ( ValidColRow(nStartCol,nStartRow) && ValidColRow(rEndCol,rEndRow) && ValidTab(nTab) ) 5567 { 5568 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 5569 bFound = maTabs[nTab]->ExtendMerge( nStartCol, nStartRow, rEndCol, rEndRow, bRefresh ); 5570 5571 if (bRefresh) 5572 RefreshAutoFilter( nStartCol, nStartRow, rEndCol, rEndRow, nTab ); 5573 } 5574 else 5575 { 5576 OSL_FAIL("ExtendMerge: invalid range"); 5577 } 5578 5579 return bFound; 5580 } 5581 5582 bool ScDocument::ExtendMerge( ScRange& rRange, bool bRefresh ) 5583 { 5584 bool bFound = false; 5585 SCTAB nStartTab = rRange.aStart.Tab(); 5586 SCTAB nEndTab = rRange.aEnd.Tab(); 5587 SCCOL nEndCol = rRange.aEnd.Col(); 5588 SCROW nEndRow = rRange.aEnd.Row(); 5589 5590 PutInOrder( nStartTab, nEndTab ); 5591 for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ ) 5592 { 5593 SCCOL nExtendCol = rRange.aEnd.Col(); 5594 SCROW nExtendRow = rRange.aEnd.Row(); 5595 if (ExtendMerge( rRange.aStart.Col(), rRange.aStart.Row(), 5596 nExtendCol, nExtendRow, 5597 nTab, bRefresh ) ) 5598 { 5599 bFound = true; 5600 if (nExtendCol > nEndCol) nEndCol = nExtendCol; 5601 if (nExtendRow > nEndRow) nEndRow = nExtendRow; 5602 } 5603 } 5604 5605 rRange.aEnd.SetCol(nEndCol); 5606 rRange.aEnd.SetRow(nEndRow); 5607 5608 return bFound; 5609 } 5610 5611 void ScDocument::ExtendTotalMerge( ScRange& rRange ) const 5612 { 5613 // Extend range to merged cells without including any new non-overlapped cells 5614 ScRange aExt = rRange; 5615 // ExtendMerge() is non-const, but called without refresh. 5616 if (const_cast<ScDocument*>(this)->ExtendMerge( aExt )) 5617 { 5618 if ( aExt.aEnd.Row() > rRange.aEnd.Row() ) 5619 { 5620 ScRange aTest = aExt; 5621 aTest.aStart.SetRow( rRange.aEnd.Row() + 1 ); 5622 if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) ) 5623 aExt.aEnd.SetRow(rRange.aEnd.Row()); 5624 } 5625 if ( aExt.aEnd.Col() > rRange.aEnd.Col() ) 5626 { 5627 ScRange aTest = aExt; 5628 aTest.aStart.SetCol( rRange.aEnd.Col() + 1 ); 5629 if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) ) 5630 aExt.aEnd.SetCol(rRange.aEnd.Col()); 5631 } 5632 5633 rRange = aExt; 5634 } 5635 } 5636 5637 void ScDocument::ExtendOverlapped( ScRange& rRange ) const 5638 { 5639 SCTAB nStartTab = rRange.aStart.Tab(); 5640 SCTAB nEndTab = rRange.aEnd.Tab(); 5641 SCCOL nStartCol = rRange.aStart.Col(); 5642 SCROW nStartRow = rRange.aStart.Row(); 5643 5644 PutInOrder( nStartTab, nEndTab ); 5645 for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ ) 5646 { 5647 SCCOL nExtendCol = rRange.aStart.Col(); 5648 SCROW nExtendRow = rRange.aStart.Row(); 5649 ExtendOverlapped( nExtendCol, nExtendRow, 5650 rRange.aEnd.Col(), rRange.aEnd.Row(), nTab ); 5651 if (nExtendCol < nStartCol) 5652 { 5653 nStartCol = nExtendCol; 5654 } 5655 if (nExtendRow < nStartRow) 5656 { 5657 nStartRow = nExtendRow; 5658 } 5659 } 5660 5661 rRange.aStart.SetCol(nStartCol); 5662 rRange.aStart.SetRow(nStartRow); 5663 } 5664 5665 bool ScDocument::RefreshAutoFilter( SCCOL nStartCol, SCROW nStartRow, 5666 SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) 5667 { 5668 SCTAB nDBTab; 5669 SCCOL nDBStartCol; 5670 SCROW nDBStartRow; 5671 SCCOL nDBEndCol; 5672 SCROW nDBEndRow; 5673 5674 // Delete Autofilter 5675 5676 bool bChange = RemoveFlagsTab( nStartCol,nStartRow, nEndCol,nEndRow, nTab, ScMF::Auto ); 5677 5678 // Set Autofilter 5679 5680 const ScDBData* pData = nullptr; 5681 ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs(); 5682 for (const auto& rxDB : rDBs) 5683 { 5684 if (rxDB->HasAutoFilter()) 5685 { 5686 rxDB->GetArea(nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow); 5687 if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow && 5688 nDBStartCol<=nEndCol && nDBEndCol>=nStartCol ) 5689 { 5690 if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow, 5691 nDBTab, ScMF::Auto )) 5692 bChange = true; 5693 } 5694 } 5695 } 5696 if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 5697 pData = maTabs[nTab]->GetAnonymousDBData(); 5698 else 5699 pData=nullptr; 5700 if (pData && pData->HasAutoFilter()) 5701 { 5702 pData->GetArea( nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow ); 5703 if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow && 5704 nDBStartCol<=nEndCol && nDBEndCol>=nStartCol ) 5705 { 5706 if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow, 5707 nDBTab, ScMF::Auto )) 5708 bChange = true; 5709 } 5710 } 5711 return bChange; 5712 } 5713 5714 void ScDocument::SkipOverlapped( SCCOL& rCol, SCROW& rRow, SCTAB nTab ) const 5715 { 5716 while (IsHorOverlapped(rCol, rRow, nTab)) 5717 --rCol; 5718 while (IsVerOverlapped(rCol, rRow, nTab)) 5719 --rRow; 5720 } 5721 5722 bool ScDocument::IsHorOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 5723 { 5724 const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ); 5725 if (pAttr) 5726 return pAttr->IsHorOverlapped(); 5727 else 5728 { 5729 OSL_FAIL("Overlapped: Attr==0"); 5730 return false; 5731 } 5732 } 5733 5734 bool ScDocument::IsVerOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const 5735 { 5736 const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ); 5737 if (pAttr) 5738 return pAttr->IsVerOverlapped(); 5739 else 5740 { 5741 OSL_FAIL("Overlapped: Attr==0"); 5742 return false; 5743 } 5744 } 5745 5746 void ScDocument::ApplySelectionFrame( const ScMarkData& rMark, 5747 const SvxBoxItem& rLineOuter, 5748 const SvxBoxInfoItem* pLineInner ) 5749 { 5750 ScRangeList aRangeList; 5751 rMark.FillRangeListWithMarks( &aRangeList, false ); 5752 size_t nRangeCount = aRangeList.size(); 5753 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5754 for (const auto& rTab : rMark) 5755 { 5756 if (rTab >= nMax) 5757 break; 5758 5759 if (maTabs[rTab]) 5760 { 5761 for ( size_t j=0; j < nRangeCount; j++ ) 5762 { 5763 const ScRange & rRange = aRangeList[ j ]; 5764 maTabs[rTab]->ApplyBlockFrame( rLineOuter, pLineInner, 5765 rRange.aStart.Col(), rRange.aStart.Row(), 5766 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5767 } 5768 } 5769 } 5770 if (rLineOuter.IsRemoveAdjacentCellBorder()) 5771 { 5772 SvxBoxItem aTmp0(rLineOuter); 5773 aTmp0.SetLine( nullptr, SvxBoxItemLine::TOP ); 5774 aTmp0.SetLine( nullptr, SvxBoxItemLine::BOTTOM ); 5775 aTmp0.SetLine( nullptr, SvxBoxItemLine::LEFT ); 5776 aTmp0.SetLine( nullptr, SvxBoxItemLine::RIGHT ); 5777 SvxBoxItem aLeft( aTmp0 ); 5778 SvxBoxItem aRight( aTmp0 ); 5779 SvxBoxItem aTop( aTmp0 ); 5780 SvxBoxItem aBottom( aTmp0 ); 5781 5782 SvxBoxInfoItem aTmp1( *pLineInner ); 5783 aTmp1.SetTable( false ); 5784 aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::HORI ); 5785 aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::VERT ); 5786 aTmp1.SetValid( SvxBoxInfoItemValidFlags::ALL, false ); 5787 aTmp1.SetValid( SvxBoxInfoItemValidFlags::DISTANCE ); 5788 SvxBoxInfoItem aLeftInfo( aTmp1 ); 5789 SvxBoxInfoItem aRightInfo( aTmp1 ); 5790 SvxBoxInfoItem aTopInfo( aTmp1 ); 5791 SvxBoxInfoItem aBottomInfo( aTmp1 ); 5792 5793 if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::TOP ) && !rLineOuter.GetTop()) 5794 aTopInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM ); 5795 5796 if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) && !rLineOuter.GetBottom()) 5797 aBottomInfo.SetValid( SvxBoxInfoItemValidFlags::TOP ); 5798 5799 if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::LEFT ) && !rLineOuter.GetLeft()) 5800 aLeftInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT ); 5801 5802 if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) && !rLineOuter.GetRight()) 5803 aRightInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT ); 5804 5805 const ScRangeList& rRangeListTopEnvelope = rMark.GetTopEnvelope(); 5806 const ScRangeList& rRangeListBottomEnvelope = rMark.GetBottomEnvelope(); 5807 const ScRangeList& rRangeListLeftEnvelope = rMark.GetLeftEnvelope(); 5808 const ScRangeList& rRangeListRightEnvelope = rMark.GetRightEnvelope(); 5809 5810 for (const auto& rTab : rMark) 5811 { 5812 if (rTab >= nMax) 5813 break; 5814 5815 if ( maTabs[rTab] ) 5816 { 5817 size_t nEnvelopeRangeCount = rRangeListTopEnvelope.size(); 5818 for ( size_t j=0; j < nEnvelopeRangeCount; j++ ) 5819 { 5820 const ScRange & rRange = rRangeListTopEnvelope[ j ]; 5821 maTabs[rTab]->ApplyBlockFrame( aTop, &aTopInfo, 5822 rRange.aStart.Col(), rRange.aStart.Row(), 5823 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5824 } 5825 nEnvelopeRangeCount = rRangeListBottomEnvelope.size(); 5826 for ( size_t j=0; j < nEnvelopeRangeCount; j++ ) 5827 { 5828 const ScRange & rRange = rRangeListBottomEnvelope[ j ]; 5829 maTabs[rTab]->ApplyBlockFrame( aBottom, &aBottomInfo, 5830 rRange.aStart.Col(), rRange.aStart.Row(), 5831 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5832 } 5833 nEnvelopeRangeCount = rRangeListLeftEnvelope.size(); 5834 for ( size_t j=0; j < nEnvelopeRangeCount; j++ ) 5835 { 5836 const ScRange & rRange = rRangeListLeftEnvelope[ j ]; 5837 maTabs[rTab]->ApplyBlockFrame( aLeft, &aLeftInfo, 5838 rRange.aStart.Col(), rRange.aStart.Row(), 5839 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5840 } 5841 nEnvelopeRangeCount = rRangeListRightEnvelope.size(); 5842 for ( size_t j=0; j < nEnvelopeRangeCount; j++ ) 5843 { 5844 const ScRange & rRange = rRangeListRightEnvelope[ j ]; 5845 maTabs[rTab]->ApplyBlockFrame( aRight, &aRightInfo, 5846 rRange.aStart.Col(), rRange.aStart.Row(), 5847 rRange.aEnd.Col(), rRange.aEnd.Row() ); 5848 } 5849 } 5850 } 5851 } 5852 } 5853 5854 void ScDocument::ApplyFrameAreaTab(const ScRange& rRange, 5855 const SvxBoxItem& rLineOuter, 5856 const SvxBoxInfoItem& rLineInner) 5857 { 5858 SCTAB nStartTab = rRange.aStart.Tab(); 5859 SCTAB nEndTab = rRange.aStart.Tab(); 5860 for (SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++) 5861 if (maTabs[nTab]) 5862 maTabs[nTab]->ApplyBlockFrame(rLineOuter, &rLineInner, 5863 rRange.aStart.Col(), rRange.aStart.Row(), 5864 rRange.aEnd.Col(), rRange.aEnd.Row()); 5865 } 5866 5867 void ScDocument::ApplySelectionPattern( const ScPatternAttr& rAttr, const ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged ) 5868 { 5869 const SfxItemSet* pSet = &rAttr.GetItemSet(); 5870 bool bSet = false; 5871 sal_uInt16 i; 5872 for (i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END && !bSet; i++) 5873 if (pSet->GetItemState(i) == SfxItemState::SET) 5874 bSet = true; 5875 5876 if (bSet) 5877 { 5878 // ApplySelectionCache needs multi mark 5879 if ( rMark.IsMarked() && !rMark.IsMultiMarked() ) 5880 { 5881 ScRange aRange; 5882 rMark.GetMarkArea( aRange ); 5883 ApplyPatternArea( aRange.aStart.Col(), aRange.aStart.Row(), 5884 aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rAttr, pDataArray, pIsChanged ); 5885 } 5886 else 5887 { 5888 SfxItemPoolCache aCache( mxPoolHelper->GetDocPool(), pSet ); 5889 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5890 for (const auto& rTab : rMark) 5891 { 5892 if (rTab >= nMax) 5893 break; 5894 if (maTabs[rTab]) 5895 maTabs[rTab]->ApplySelectionCache( &aCache, rMark, pDataArray, pIsChanged ); 5896 } 5897 } 5898 } 5899 } 5900 5901 void ScDocument::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark ) 5902 { 5903 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5904 for (const auto& rTab : rMark) 5905 { 5906 if (rTab >= nMax) 5907 break; 5908 if (maTabs[rTab]) 5909 maTabs[rTab]->ChangeSelectionIndent( bIncrement, rMark ); 5910 } 5911 } 5912 5913 void ScDocument::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark ) 5914 { 5915 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5916 for (const auto& rTab : rMark) 5917 { 5918 if (rTab >= nMax) 5919 break; 5920 if (maTabs[rTab]) 5921 maTabs[rTab]->ClearSelectionItems( pWhich, rMark ); 5922 } 5923 } 5924 5925 void ScDocument::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast ) 5926 { 5927 sc::AutoCalcSwitch aACSwitch(*this, false); 5928 5929 std::vector<ScAddress> aGroupPos; 5930 // Destroy and reconstruct listeners only if content is affected. 5931 bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag); 5932 if (bDelContent) 5933 { 5934 // Record the positions of top and/or bottom formula groups that 5935 // intersect the area borders. 5936 sc::EndListeningContext aCxt(*this); 5937 ScRangeList aRangeList; 5938 rMark.FillRangeListWithMarks( &aRangeList, false); 5939 for (size_t i = 0; i < aRangeList.size(); ++i) 5940 { 5941 const ScRange & rRange = aRangeList[i]; 5942 EndListeningIntersectedGroups( aCxt, rRange, &aGroupPos); 5943 } 5944 aCxt.purgeEmptyBroadcasters(); 5945 } 5946 5947 SCTAB nMax = static_cast<SCTAB>(maTabs.size()); 5948 for (const auto& rTab : rMark) 5949 { 5950 if (rTab >= nMax) 5951 break; 5952 if (maTabs[rTab]) 5953 maTabs[rTab]->DeleteSelection(nDelFlag, rMark, bBroadcast); 5954 } 5955 5956 if (bDelContent) 5957 { 5958 // Re-start listeners on those top bottom groups that have been split. 5959 SetNeedsListeningGroups(aGroupPos); 5960 StartNeededListeners(); 5961 5962 // If formula groups were split their listeners were destroyed and may 5963 // need to be notified now that they're restored, 5964 // ScTable::DeleteSelection() couldn't do that. 5965 if (!aGroupPos.empty()) 5966 { 5967 ScRangeList aRangeList; 5968 rMark.FillRangeListWithMarks( &aRangeList, false); 5969 for (size_t i = 0; i < aRangeList.size(); ++i) 5970 { 5971 SetDirty( aRangeList[i], true); 5972 } 5973 //Notify listeners on top and bottom of the group that has been split 5974 for (size_t i = 0; i < aGroupPos.size(); ++i) { 5975 ScFormulaCell *pFormulaCell = GetFormulaCell(aGroupPos[i]); 5976 if (pFormulaCell) 5977 pFormulaCell->SetDirty(true); 5978 } 5979 } 5980 } 5981 } 5982 5983 void ScDocument::DeleteSelectionTab( 5984 SCTAB nTab, InsertDeleteFlags nDelFlag, const ScMarkData& rMark ) 5985 { 5986 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 5987 { 5988 sc::AutoCalcSwitch aACSwitch(*this, false); 5989 5990 std::vector<ScAddress> aGroupPos; 5991 // Destroy and reconstruct listeners only if content is affected. 5992 bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag); 5993 if (bDelContent) 5994 { 5995 // Record the positions of top and/or bottom formula groups that 5996 // intersect the area borders. 5997 sc::EndListeningContext aCxt(*this); 5998 ScRangeList aRangeList; 5999 rMark.FillRangeListWithMarks( &aRangeList, false); 6000 for (size_t i = 0; i < aRangeList.size(); ++i) 6001 { 6002 const ScRange & rRange = aRangeList[i]; 6003 if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab()) 6004 { 6005 ScRange aRange( rRange); 6006 aRange.aStart.SetTab( nTab); 6007 aRange.aEnd.SetTab( nTab); 6008 EndListeningIntersectedGroups( aCxt, aRange, &aGroupPos); 6009 } 6010 } 6011 aCxt.purgeEmptyBroadcasters(); 6012 } 6013 6014 maTabs[nTab]->DeleteSelection(nDelFlag, rMark); 6015 6016 if (bDelContent) 6017 { 6018 // Re-start listeners on those top bottom groups that have been split. 6019 SetNeedsListeningGroups(aGroupPos); 6020 StartNeededListeners(); 6021 6022 // If formula groups were split their listeners were destroyed and may 6023 // need to be notified now that they're restored, 6024 // ScTable::DeleteSelection() couldn't do that. 6025 if (!aGroupPos.empty()) 6026 { 6027 ScRangeList aRangeList; 6028 rMark.FillRangeListWithMarks( &aRangeList, false); 6029 for (size_t i = 0; i < aRangeList.size(); ++i) 6030 { 6031 const ScRange & rRange = aRangeList[i]; 6032 if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab()) 6033 { 6034 ScRange aRange( rRange); 6035 aRange.aStart.SetTab( nTab); 6036 aRange.aEnd.SetTab( nTab); 6037 SetDirty( aRange, true); 6038 } 6039 } 6040 } 6041 } 6042 } 6043 else 6044 { 6045 OSL_FAIL("wrong table"); 6046 } 6047 } 6048 6049 ScPatternAttr* ScDocument::GetDefPattern() const 6050 { 6051 return const_cast<ScPatternAttr*>(&mxPoolHelper->GetDocPool()->GetDefaultItem(ATTR_PATTERN)); 6052 } 6053 6054 ScDocumentPool* ScDocument::GetPool() 6055 { 6056 return mxPoolHelper->GetDocPool(); 6057 } 6058 6059 ScStyleSheetPool* ScDocument::GetStyleSheetPool() const 6060 { 6061 return mxPoolHelper->GetStylePool(); 6062 } 6063 6064 SCSIZE ScDocument::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab, 6065 SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, ScDirection eDir ) 6066 { 6067 PutInOrder(nStartCol, nEndCol); 6068 PutInOrder(nStartRow, nEndRow); 6069 PutInOrder(nStartTab, nEndTab); 6070 if (ValidTab(nStartTab) && nStartTab < static_cast<SCTAB>(maTabs.size())) 6071 { 6072 if (maTabs[nStartTab]) 6073 return maTabs[nStartTab]->GetEmptyLinesInBlock(nStartCol, nStartRow, nEndCol, nEndRow, eDir); 6074 else 6075 return 0; 6076 } 6077 else 6078 return 0; 6079 } 6080 6081 void ScDocument::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, ScMoveDirection eDirection ) const 6082 { 6083 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6084 maTabs[nTab]->FindAreaPos( rCol, rRow, eDirection ); 6085 } 6086 6087 void ScDocument::GetNextPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCCOL nMovX, SCROW nMovY, 6088 bool bMarked, bool bUnprotected, const ScMarkData& rMark ) const 6089 { 6090 OSL_ENSURE( !nMovX || !nMovY, "GetNextPos: only X or Y" ); 6091 6092 ScMarkData aCopyMark = rMark; 6093 aCopyMark.SetMarking(false); 6094 aCopyMark.MarkToMulti(); 6095 6096 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6097 maTabs[nTab]->GetNextPos( rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark ); 6098 } 6099 6100 // Data operations 6101 6102 void ScDocument::UpdStlShtPtrsFrmNms() 6103 { 6104 ScDocumentPool* pPool = mxPoolHelper->GetDocPool(); 6105 6106 for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN)) 6107 { 6108 auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem)); 6109 if (pPattern) 6110 pPattern->UpdateStyleSheet(this); 6111 } 6112 const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).UpdateStyleSheet(this); 6113 } 6114 6115 void ScDocument::StylesToNames() 6116 { 6117 ScDocumentPool* pPool = mxPoolHelper->GetDocPool(); 6118 6119 for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN)) 6120 { 6121 auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem)); 6122 if (pPattern) 6123 pPattern->StyleToName(); 6124 } 6125 const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).StyleToName(); 6126 } 6127 6128 sal_uLong ScDocument::GetCellCount() const 6129 { 6130 sal_uLong nCellCount = 0; 6131 6132 for (const auto& a : maTabs) 6133 { 6134 if (a) 6135 nCellCount += a->GetCellCount(); 6136 } 6137 6138 return nCellCount; 6139 } 6140 6141 sal_uLong ScDocument::GetFormulaGroupCount() const 6142 { 6143 sal_uLong nFormulaGroupCount = 0; 6144 6145 ScFormulaGroupIterator aIter( const_cast<ScDocument*>(this) ); 6146 for ( sc::FormulaGroupEntry* ptr = aIter.first(); ptr; ptr = aIter.next()) 6147 { 6148 nFormulaGroupCount++; 6149 } 6150 6151 return nFormulaGroupCount; 6152 } 6153 6154 sal_uLong ScDocument::GetCodeCount() const 6155 { 6156 sal_uLong nCodeCount = 0; 6157 6158 for (const auto& a : maTabs) 6159 { 6160 if (a) 6161 nCodeCount += a->GetCodeCount(); 6162 } 6163 6164 return nCodeCount; 6165 } 6166 6167 void ScDocument::PageStyleModified( SCTAB nTab, const OUString& rNewName ) 6168 { 6169 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6170 maTabs[nTab]->PageStyleModified( rNewName ); 6171 } 6172 6173 void ScDocument::SetPageStyle( SCTAB nTab, const OUString& rName ) 6174 { 6175 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6176 maTabs[nTab]->SetPageStyle( rName ); 6177 } 6178 6179 const OUString ScDocument::GetPageStyle( SCTAB nTab ) const 6180 { 6181 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6182 return maTabs[nTab]->GetPageStyle(); 6183 6184 return OUString(); 6185 } 6186 6187 void ScDocument::SetPageSize( SCTAB nTab, const Size& rSize ) 6188 { 6189 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6190 maTabs[nTab]->SetPageSize( rSize ); 6191 } 6192 6193 Size ScDocument::GetPageSize( SCTAB nTab ) const 6194 { 6195 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6196 return maTabs[nTab]->GetPageSize(); 6197 6198 OSL_FAIL("invalid tab"); 6199 return Size(); 6200 } 6201 6202 void ScDocument::SetRepeatArea( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow ) 6203 { 6204 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6205 maTabs[nTab]->SetRepeatArea( nStartCol, nEndCol, nStartRow, nEndRow ); 6206 } 6207 6208 void ScDocument::InvalidatePageBreaks(SCTAB nTab) 6209 { 6210 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6211 maTabs[nTab]->InvalidatePageBreaks(); 6212 } 6213 6214 void ScDocument::UpdatePageBreaks( SCTAB nTab, const ScRange* pUserArea ) 6215 { 6216 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6217 maTabs[nTab]->UpdatePageBreaks( pUserArea ); 6218 } 6219 6220 void ScDocument::RemoveManualBreaks( SCTAB nTab ) 6221 { 6222 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6223 maTabs[nTab]->RemoveManualBreaks(); 6224 } 6225 6226 bool ScDocument::HasManualBreaks( SCTAB nTab ) const 6227 { 6228 if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] ) 6229 return maTabs[nTab]->HasManualBreaks(); 6230 6231 OSL_FAIL("invalid tab"); 6232 return false; 6233 } 6234 6235 void ScDocument::GetDocStat( ScDocStat& rDocStat ) 6236 { 6237 rDocStat.nTableCount = GetTableCount(); 6238 rDocStat.aDocName = aDocName; 6239 rDocStat.nFormulaCount = GetFormulaGroupCount(); 6240 rDocStat.nCellCount = GetCellCount(); 6241 } 6242 6243 bool ScDocument::HasPrintRange() 6244 { 6245 bool bResult = false; 6246 6247 for (const auto& a : maTabs) 6248 { 6249 if (!a) 6250 continue; 6251 bResult = a->IsPrintEntireSheet() || (a->GetPrintRangeCount() > 0); 6252 if (bResult) 6253 break; 6254 } 6255 6256 return bResult; 6257 } 6258 6259 bool ScDocument::IsPrintEntireSheet( SCTAB nTab ) const 6260 { 6261 return (ValidTab(nTab) ) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsPrintEntireSheet(); 6262 } 6263 6264 sal_uInt16 ScDocument::GetPrintRangeCount( SCTAB nTab ) 6265 { 6266 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6267 return maTabs[nTab]->GetPrintRangeCount(); 6268 6269 return 0; 6270 } 6271 6272 const ScRange* ScDocument::GetPrintRange( SCTAB nTab, sal_uInt16 nPos ) 6273 { 6274 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6275 return maTabs[nTab]->GetPrintRange(nPos); 6276 6277 return nullptr; 6278 } 6279 6280 const ScRange* ScDocument::GetRepeatColRange( SCTAB nTab ) 6281 { 6282 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6283 return maTabs[nTab]->GetRepeatColRange(); 6284 6285 return nullptr; 6286 } 6287 6288 const ScRange* ScDocument::GetRepeatRowRange( SCTAB nTab ) 6289 { 6290 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6291 return maTabs[nTab]->GetRepeatRowRange(); 6292 6293 return nullptr; 6294 } 6295 6296 void ScDocument::ClearPrintRanges( SCTAB nTab ) 6297 { 6298 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6299 maTabs[nTab]->ClearPrintRanges(); 6300 } 6301 6302 void ScDocument::AddPrintRange( SCTAB nTab, const ScRange& rNew ) 6303 { 6304 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6305 maTabs[nTab]->AddPrintRange( rNew ); 6306 } 6307 6308 void ScDocument::SetPrintEntireSheet( SCTAB nTab ) 6309 { 6310 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6311 maTabs[nTab]->SetPrintEntireSheet(); 6312 } 6313 6314 void ScDocument::SetRepeatColRange( SCTAB nTab, std::unique_ptr<ScRange> pNew ) 6315 { 6316 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6317 maTabs[nTab]->SetRepeatColRange( std::move(pNew) ); 6318 } 6319 6320 void ScDocument::SetRepeatRowRange( SCTAB nTab, std::unique_ptr<ScRange> pNew ) 6321 { 6322 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6323 maTabs[nTab]->SetRepeatRowRange( std::move(pNew) ); 6324 } 6325 6326 std::unique_ptr<ScPrintRangeSaver> ScDocument::CreatePrintRangeSaver() const 6327 { 6328 const SCTAB nCount = static_cast<SCTAB>(maTabs.size()); 6329 std::unique_ptr<ScPrintRangeSaver> pNew(new ScPrintRangeSaver( nCount )); 6330 for (SCTAB i=0; i<nCount; i++) 6331 if (maTabs[i]) 6332 maTabs[i]->FillPrintSaver( pNew->GetTabData(i) ); 6333 return pNew; 6334 } 6335 6336 void ScDocument::RestorePrintRanges( const ScPrintRangeSaver& rSaver ) 6337 { 6338 const SCTAB nCount = rSaver.GetTabCount(); 6339 const SCTAB maxIndex = std::min(nCount, static_cast<SCTAB>(maTabs.size())); 6340 for (SCTAB i=0; i<maxIndex; i++) 6341 if (maTabs[i]) 6342 maTabs[i]->RestorePrintRanges( rSaver.GetTabData(i) ); 6343 } 6344 6345 bool ScDocument::NeedPageResetAfterTab( SCTAB nTab ) const 6346 { 6347 // The page number count restarts at a sheet, if another template is set at 6348 // the preceding one (only compare names) and if a pagenumber is specified (not 0) 6349 6350 if ( nTab + 1 < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab+1] ) 6351 { 6352 const OUString & rNew = maTabs[nTab+1]->GetPageStyle(); 6353 if ( rNew != maTabs[nTab]->GetPageStyle() ) 6354 { 6355 SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( rNew, SfxStyleFamily::Page ); 6356 if ( pStyle ) 6357 { 6358 const SfxItemSet& rSet = pStyle->GetItemSet(); 6359 sal_uInt16 nFirst = rSet.Get(ATTR_PAGE_FIRSTPAGENO).GetValue(); 6360 if ( nFirst != 0 ) 6361 return true; // Specify page number in new template 6362 } 6363 } 6364 } 6365 6366 return false; // otherwise not 6367 } 6368 6369 SfxUndoManager* ScDocument::GetUndoManager() 6370 { 6371 if (!mpUndoManager) 6372 { 6373 // to support enhanced text edit for draw objects, use an SdrUndoManager 6374 ScMutationGuard aGuard(this, ScMutationGuardFlags::CORE); 6375 6376 SdrUndoManager* pUndoManager = new SdrUndoManager; 6377 pUndoManager->SetDocShell(GetDocumentShell()); 6378 mpUndoManager = pUndoManager; 6379 } 6380 6381 return mpUndoManager; 6382 } 6383 6384 ScRowBreakIterator* ScDocument::GetRowBreakIterator(SCTAB nTab) const 6385 { 6386 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6387 return new ScRowBreakIterator(maTabs[nTab]->maRowPageBreaks); 6388 return nullptr; 6389 } 6390 6391 void ScDocument::AddSubTotalCell(ScFormulaCell* pCell) 6392 { 6393 maSubTotalCells.insert(pCell); 6394 } 6395 6396 void ScDocument::RemoveSubTotalCell(ScFormulaCell* pCell) 6397 { 6398 maSubTotalCells.erase(pCell); 6399 } 6400 6401 namespace { 6402 6403 bool lcl_hasDirtyRange(ScFormulaCell* pCell, const ScRange& rDirtyRange) 6404 { 6405 ScDetectiveRefIter aRefIter(pCell); 6406 ScRange aRange; 6407 while (aRefIter.GetNextRef(aRange)) 6408 { 6409 if (aRange.Intersects(rDirtyRange)) 6410 return true; 6411 } 6412 return false; 6413 } 6414 6415 } 6416 6417 void ScDocument::SetSubTotalCellsDirty(const ScRange& rDirtyRange) 6418 { 6419 // to update the list by skipping cells that no longer contain subtotal function. 6420 set<ScFormulaCell*> aNewSet; 6421 6422 bool bOldRecalc = GetAutoCalc(); 6423 SetAutoCalc(false); 6424 for (ScFormulaCell* pCell : maSubTotalCells) 6425 { 6426 if (pCell->IsSubTotal()) 6427 { 6428 aNewSet.insert(pCell); 6429 if (lcl_hasDirtyRange(pCell, rDirtyRange)) 6430 pCell->SetDirty(); 6431 } 6432 } 6433 6434 SetAutoCalc(bOldRecalc); 6435 maSubTotalCells.swap(aNewSet); // update the list. 6436 } 6437 6438 sal_uInt16 ScDocument::GetTextWidth( const ScAddress& rPos ) const 6439 { 6440 SCTAB nTab = rPos.Tab(); 6441 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6442 return maTabs[nTab]->GetTextWidth(rPos.Col(), rPos.Row()); 6443 6444 return 0; 6445 } 6446 6447 SvtScriptType ScDocument::GetScriptType( const ScAddress& rPos ) const 6448 { 6449 SCTAB nTab = rPos.Tab(); 6450 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6451 return maTabs[nTab]->GetScriptType(rPos.Col(), rPos.Row()); 6452 6453 return SvtScriptType::NONE; 6454 } 6455 6456 void ScDocument::SetScriptType( const ScAddress& rPos, SvtScriptType nType ) 6457 { 6458 SCTAB nTab = rPos.Tab(); 6459 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]) 6460 maTabs[nTab]->SetScriptType(rPos.Col(), rPos.Row(), nType); 6461 } 6462 6463 void ScDocument::EnableUndo( bool bVal ) 6464 { 6465 // The undo manager increases lock count every time undo is disabled. 6466 // Because of this, we shouldn't disable undo unless it's currently 6467 // enabled, or else re-enabling it may not actually re-enable undo unless 6468 // the lock count becomes zero. 6469 6470 if (bVal != GetUndoManager()->IsUndoEnabled()) 6471 { 6472 GetUndoManager()->EnableUndo(bVal); 6473 if( mpDrawLayer ) mpDrawLayer->EnableUndo(bVal); 6474 } 6475 6476 mbUndoEnabled = bVal; 6477 } 6478 6479 void ScDocument::EnableUserInteraction( bool bVal ) 6480 { 6481 mbUserInteractionEnabled = bVal; 6482 } 6483 6484 bool ScDocument::IsInVBAMode() const 6485 { 6486 if (!mpShell) 6487 return false; 6488 6489 try 6490 { 6491 uno::Reference<script::vba::XVBACompatibility> xVBA( 6492 mpShell->GetBasicContainer(), uno::UNO_QUERY); 6493 6494 return xVBA.is() && xVBA->getVBACompatibilityMode(); 6495 } 6496 catch (const lang::NotInitializedException&) {} 6497 6498 return false; 6499 } 6500 6501 ScPostIt* ScDocument::GetNote(const ScAddress& rPos) 6502 { 6503 return GetNote(rPos.Col(), rPos.Row(), rPos.Tab()); 6504 } 6505 6506 ScPostIt* ScDocument::GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab) 6507 { 6508 if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && 6509 nCol < maTabs[nTab]->GetAllocatedColumnsCount()) 6510 return maTabs[nTab]->aCol[nCol].GetCellNote(nRow); 6511 else 6512 return nullptr; 6513 6514 } 6515 6516 void ScDocument::SetNote(const ScAddress& rPos, std::unique_ptr<ScPostIt> pNote) 6517 { 6518 return SetNote(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pNote)); 6519 } 6520 6521 void ScDocument::SetNote(SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPostIt> pNote) 6522 { 6523 return maTabs[nTab]->CreateColumnIfNotExists(nCol).SetCellNote(nRow, std::move(pNote)); 6524 } 6525 6526 bool ScDocument::HasNote(const ScAddress& rPos) const 6527 { 6528 return HasNote(rPos.Col(), rPos.Row(), rPos.Tab()); 6529 } 6530 6531 bool ScDocument::HasNote(SCCOL nCol, SCROW nRow, SCTAB nTab) const 6532 { 6533 if (!ValidColRow(nCol, nRow)) 6534 return false; 6535 6536 const ScTable* pTab = FetchTable(nTab); 6537 if (!pTab) 6538 return false; 6539 6540 if (nCol >= pTab->GetAllocatedColumnsCount()) 6541 return false; 6542 6543 const ScPostIt* pNote = pTab->aCol[nCol].GetCellNote(nRow); 6544 return pNote != nullptr; 6545 } 6546 6547 bool ScDocument::HasColNotes(SCCOL nCol, SCTAB nTab) const 6548 { 6549 if (!ValidCol(nCol)) 6550 return false; 6551 6552 const ScTable* pTab = FetchTable(nTab); 6553 if (!pTab) 6554 return false; 6555 6556 return pTab->aCol[nCol].HasCellNotes(); 6557 } 6558 6559 bool ScDocument::HasTabNotes(SCTAB nTab) const 6560 { 6561 const ScTable* pTab = FetchTable(nTab); 6562 6563 if ( !pTab ) 6564 return false; 6565 6566 for (SCCOL nCol=0, nColSize = pTab->aCol.size(); nCol < nColSize; ++nCol) 6567 if ( HasColNotes(nCol, nTab) ) 6568 return true; 6569 6570 return false; 6571 } 6572 6573 bool ScDocument::HasNotes() const 6574 { 6575 for (SCTAB i = 0; i <= MAXTAB; ++i) 6576 { 6577 if (HasTabNotes(i)) 6578 return true; 6579 } 6580 return false; 6581 } 6582 6583 std::unique_ptr<ScPostIt> ScDocument::ReleaseNote(const ScAddress& rPos) 6584 { 6585 ScTable* pTab = FetchTable(rPos.Tab()); 6586 if (!pTab) 6587 return nullptr; 6588 6589 return pTab->ReleaseNote(rPos.Col(), rPos.Row()); 6590 } 6591 6592 ScPostIt* ScDocument::GetOrCreateNote(const ScAddress& rPos) 6593 { 6594 if (HasNote(rPos)) 6595 return GetNote(rPos); 6596 else 6597 return CreateNote(rPos); 6598 } 6599 ScPostIt* ScDocument::CreateNote(const ScAddress& rPos) 6600 { 6601 ScPostIt* pPostIt = new ScPostIt(*this, rPos); 6602 SetNote(rPos, std::unique_ptr<ScPostIt>(pPostIt)); 6603 return pPostIt; 6604 } 6605 6606 size_t ScDocument::GetNoteCount( SCTAB nTab, SCCOL nCol ) const 6607 { 6608 const ScTable* pTab = FetchTable(nTab); 6609 if (!pTab) 6610 return 0; 6611 6612 return pTab->GetNoteCount(nCol); 6613 } 6614 6615 void ScDocument::CreateAllNoteCaptions() 6616 { 6617 for (const auto& a : maTabs) 6618 { 6619 if (a) 6620 a->CreateAllNoteCaptions(); 6621 } 6622 } 6623 6624 void ScDocument::ForgetNoteCaptions( const ScRangeList& rRanges, bool bPreserveData ) 6625 { 6626 for (size_t i = 0, n = rRanges.size(); i < n; ++i) 6627 { 6628 const ScRange & rRange = rRanges[i]; 6629 const ScAddress& s = rRange.aStart; 6630 const ScAddress& e = rRange.aEnd; 6631 for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab) 6632 { 6633 ScTable* pTab = FetchTable(nTab); 6634 if (!pTab) 6635 continue; 6636 6637 pTab->ForgetNoteCaptions(s.Col(), s.Row(), e.Col(), e.Row(), bPreserveData); 6638 } 6639 } 6640 } 6641 6642 CommentCaptionState ScDocument::GetAllNoteCaptionsState( const ScRangeList& rRanges ) 6643 { 6644 CommentCaptionState aTmpState = CommentCaptionState::ALLHIDDEN; 6645 CommentCaptionState aState = CommentCaptionState::ALLHIDDEN; 6646 bool bFirstControl = true; 6647 std::vector<sc::NoteEntry> aNotes; 6648 6649 for (size_t i = 0, n = rRanges.size(); i < n; ++i) 6650 { 6651 const ScRange & rRange = rRanges[i]; 6652 6653 for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab ) 6654 { 6655 aState = maTabs[nTab]->GetAllNoteCaptionsState( rRange, aNotes ); 6656 6657 if (aState == CommentCaptionState::MIXED) 6658 return aState; 6659 6660 if (bFirstControl) // it is possible that a range is ALLSHOWN, another range is ALLHIDDEN, 6661 { // we have to detect that situation as mixed. 6662 aTmpState = aState; 6663 bFirstControl = false; 6664 } 6665 else if(aTmpState != aState) 6666 { 6667 aState = CommentCaptionState::MIXED; 6668 return aState; 6669 } 6670 } 6671 } 6672 return aState; 6673 } 6674 6675 ScAddress ScDocument::GetNotePosition( size_t nIndex ) const 6676 { 6677 for (size_t nTab = 0; nTab < maTabs.size(); ++nTab) 6678 { 6679 for (SCCOL nCol : GetColumnsRange(nTab, 0, MAXCOL)) 6680 { 6681 size_t nColNoteCount = GetNoteCount(nTab, nCol); 6682 if (!nColNoteCount) 6683 continue; 6684 6685 if (nIndex >= nColNoteCount) 6686 { 6687 nIndex -= nColNoteCount; 6688 continue; 6689 } 6690 6691 SCROW nRow = GetNotePosition(nTab, nCol, nIndex); 6692 if (nRow >= 0) 6693 return ScAddress(nCol, nRow, nTab); 6694 6695 OSL_FAIL("note not found"); 6696 return ScAddress::INITIALIZE_INVALID; 6697 } 6698 } 6699 6700 OSL_FAIL("note not found"); 6701 return ScAddress::INITIALIZE_INVALID; 6702 } 6703 6704 ScAddress ScDocument::GetNotePosition( size_t nIndex, SCTAB nTab ) const 6705 { 6706 for (SCCOL nCol : GetColumnsRange(nTab, 0, MAXCOL)) 6707 { 6708 size_t nColNoteCount = GetNoteCount(nTab, nCol); 6709 if (!nColNoteCount) 6710 continue; 6711 6712 if (nIndex >= nColNoteCount) 6713 { 6714 nIndex -= nColNoteCount; 6715 continue; 6716 } 6717 6718 SCROW nRow = GetNotePosition(nTab, nCol, nIndex); 6719 if (nRow >= 0) 6720 return ScAddress(nCol, nRow, nTab); 6721 6722 OSL_FAIL("note not found"); 6723 return ScAddress::INITIALIZE_INVALID; 6724 } 6725 6726 OSL_FAIL("note not found"); 6727 return ScAddress::INITIALIZE_INVALID; 6728 } 6729 6730 SCROW ScDocument::GetNotePosition( SCTAB nTab, SCCOL nCol, size_t nIndex ) const 6731 { 6732 const ScTable* pTab = FetchTable(nTab); 6733 if (!pTab) 6734 return -1; 6735 6736 return pTab->GetNotePosition(nCol, nIndex); 6737 } 6738 6739 void ScDocument::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const 6740 { 6741 for (const auto & pTab : maTabs) 6742 { 6743 if (!pTab) 6744 continue; 6745 6746 pTab->GetAllNoteEntries(rNotes); 6747 } 6748 } 6749 6750 void ScDocument::GetAllNoteEntries( SCTAB nTab, std::vector<sc::NoteEntry>& rNotes ) const 6751 { 6752 const ScTable* pTab = FetchTable(nTab); 6753 if (!pTab) 6754 return; 6755 6756 return pTab->GetAllNoteEntries( rNotes ); 6757 } 6758 6759 void ScDocument::GetNotesInRange( const ScRangeList& rRangeList, std::vector<sc::NoteEntry>& rNotes ) const 6760 { 6761 for( size_t i = 0; i < rRangeList.size(); ++i) 6762 { 6763 const ScRange & rRange = rRangeList[i]; 6764 for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab ) 6765 { 6766 maTabs[nTab]->GetNotesInRange( rRange, rNotes ); 6767 } 6768 } 6769 } 6770 6771 void ScDocument::GetUnprotectedCells( ScRangeList& rRangeList, SCTAB nTab ) const 6772 { 6773 maTabs[nTab]->GetUnprotectedCells( rRangeList ); 6774 } 6775 6776 bool ScDocument::ContainsNotesInRange( const ScRangeList& rRangeList ) const 6777 { 6778 for( size_t i = 0; i < rRangeList.size(); ++i) 6779 { 6780 const ScRange & rRange = rRangeList[i]; 6781 for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab ) 6782 { 6783 bool bContainsNote = maTabs[nTab]->ContainsNotesInRange( rRange ); 6784 if(bContainsNote) 6785 return true; 6786 } 6787 } 6788 6789 return false; 6790 } 6791 6792 void ScDocument::SetAutoNameCache( std::unique_ptr<ScAutoNameCache> pCache ) 6793 { 6794 pAutoNameCache = std::move(pCache); 6795 } 6796 6797 thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific; 6798 6799 ScRecursionHelper& ScDocument::GetRecursionHelper() 6800 { 6801 if (!IsThreadedGroupCalcInProgress()) 6802 { 6803 if (!maNonThreaded.pRecursionHelper) 6804 maNonThreaded.pRecursionHelper = CreateRecursionHelperInstance(); 6805 return *maNonThreaded.pRecursionHelper; 6806 } 6807 else 6808 { 6809 if (!maThreadSpecific.pRecursionHelper) 6810 maThreadSpecific.pRecursionHelper = CreateRecursionHelperInstance(); 6811 return *maThreadSpecific.pRecursionHelper; 6812 } 6813 } 6814 6815 void ScDocumentThreadSpecific::SetupFromNonThreadedData(const ScDocumentThreadSpecific& /*rNonThreadedData*/) 6816 { 6817 // What about the recursion helper? 6818 // Copy the lookup cache? 6819 } 6820 6821 void ScDocumentThreadSpecific::MergeBackIntoNonThreadedData(ScDocumentThreadSpecific& /*rNonThreadedData*/) 6822 { 6823 // What about recursion helper and lookup cache? 6824 } 6825 6826 void ScDocument::SetupFromNonThreadedContext(ScInterpreterContext& /*threadedContext*/, int /*threadNumber*/) 6827 { 6828 // lookup cache is now only in pooled ScInterpreterContext's 6829 } 6830 6831 void ScDocument::MergeBackIntoNonThreadedContext(ScInterpreterContext& threadedContext, int /*threadNumber*/) 6832 { 6833 // Move data from a context used by a calculation thread to the main thread's context. 6834 // Called from the main thread after the calculation thread has already finished. 6835 assert(!IsThreadedGroupCalcInProgress()); 6836 maInterpreterContext.maDelayedSetNumberFormat.insert( 6837 maInterpreterContext.maDelayedSetNumberFormat.end(), 6838 std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.begin()), 6839 std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.end())); 6840 // lookup cache is now only in pooled ScInterpreterContext's 6841 } 6842 6843 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 6844
