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 <comphelper/lok.hxx> 23 #include <o3tl/safeint.hxx> 24 #include <o3tl/string_view.hxx> 25 #include <sfx2/app.hxx> 26 #include <editeng/editobj.hxx> 27 #include <editeng/justifyitem.hxx> 28 #include <sfx2/linkmgr.hxx> 29 #include <sfx2/bindings.hxx> 30 #include <utility> 31 #include <vcl/weld.hxx> 32 #include <vcl/stdtext.hxx> 33 #include <vcl/svapp.hxx> 34 #include <svx/svdocapt.hxx> 35 #include <sal/log.hxx> 36 #include <unotools/charclass.hxx> 37 #include <osl/diagnose.h> 38 39 #include <com/sun/star/container/XNameContainer.hpp> 40 #include <com/sun/star/script/ModuleType.hpp> 41 #include <com/sun/star/script/XLibraryContainer.hpp> 42 #include <com/sun/star/script/vba/XVBAModuleInfo.hpp> 43 44 #include <docfunc.hxx> 45 46 #include <sc.hrc> 47 #include <strings.hrc> 48 49 #include <arealink.hxx> 50 #include <attrib.hxx> 51 #include <dociter.hxx> 52 #include <autoform.hxx> 53 #include <formulacell.hxx> 54 #include <cellmergeoption.hxx> 55 #include <detdata.hxx> 56 #include <detfunc.hxx> 57 #include <docpool.hxx> 58 #include <docsh.hxx> 59 #include <drwlayer.hxx> 60 #include <editutil.hxx> 61 #include <globstr.hrc> 62 #include <olinetab.hxx> 63 #include <patattr.hxx> 64 #include <rangenam.hxx> 65 #include <refundo.hxx> 66 #include <scresid.hxx> 67 #include <stlpool.hxx> 68 #include <stlsheet.hxx> 69 #include <tablink.hxx> 70 #include <tabvwsh.hxx> 71 #include <uiitems.hxx> 72 #include <undoblk.hxx> 73 #include <undocell.hxx> 74 #include <undodraw.hxx> 75 #include <undotab.hxx> 76 #include <sizedev.hxx> 77 #include <scmod.hxx> 78 #include <inputhdl.hxx> 79 #include <editable.hxx> 80 #include <compiler.hxx> 81 #include <scui_def.hxx> 82 #include <tabprotection.hxx> 83 #include <clipparam.hxx> 84 #include <externalrefmgr.hxx> 85 #include <undorangename.hxx> 86 #include <progress.hxx> 87 #include <dpobject.hxx> 88 #include <stringutil.hxx> 89 #include <cellvalue.hxx> 90 #include <tokenarray.hxx> 91 #include <rowheightcontext.hxx> 92 #include <cellvalues.hxx> 93 #include <undoconvert.hxx> 94 #include <docfuncutil.hxx> 95 #include <sheetevents.hxx> 96 #include <conditio.hxx> 97 #include <columnspanset.hxx> 98 #include <validat.hxx> 99 #include <SparklineGroup.hxx> 100 #include <SparklineAttributes.hxx> 101 #include <SparklineData.hxx> 102 #include <undo/UndoInsertSparkline.hxx> 103 #include <undo/UndoDeleteSparkline.hxx> 104 #include <undo/UndoDeleteSparklineGroup.hxx> 105 #include <undo/UndoEditSparklineGroup.hxx> 106 #include <undo/UndoUngroupSparklines.hxx> 107 #include <undo/UndoGroupSparklines.hxx> 108 #include <undo/UndoEditSparkline.hxx> 109 #include <config_features.h> 110 111 #include <memory> 112 #include <basic/basmgr.hxx> 113 #include <set> 114 #include <vector> 115 #include <sfx2/viewfrm.hxx> 116 117 using namespace com::sun::star; 118 using ::std::vector; 119 120 void ScDocFunc::NotifyDrawUndo( std::unique_ptr<SdrUndoAction> pUndoAction) 121 { 122 // #i101118# if drawing layer collects the undo actions, add it there 123 ScDrawLayer* pDrawLayer = rDocShell.GetDocument().GetDrawLayer(); 124 if( pDrawLayer && pDrawLayer->IsRecording() ) 125 pDrawLayer->AddCalcUndo( std::move(pUndoAction) ); 126 else 127 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDraw>( std::move(pUndoAction), &rDocShell ) ); 128 rDocShell.SetDrawModified(); 129 130 // the affected sheet isn't known, so all stream positions are invalidated 131 ScDocument& rDoc = rDocShell.GetDocument(); 132 SCTAB nTabCount = rDoc.GetTableCount(); 133 for (SCTAB nTab=0; nTab<nTabCount; nTab++) 134 rDoc.SetStreamValid(nTab, false); 135 } 136 137 // paint row above the range (because of lines after AdjustRowHeight) 138 139 static void lcl_PaintAbove( ScDocShell& rDocShell, const ScRange& rRange ) 140 { 141 SCROW nRow = rRange.aStart.Row(); 142 if ( nRow > 0 ) 143 { 144 SCTAB nTab = rRange.aStart.Tab(); //! all of them? 145 --nRow; 146 ScDocument& rDoc = rDocShell.GetDocument(); 147 rDocShell.PostPaint( ScRange(0,nRow,nTab,rDoc.MaxCol(),nRow,nTab), PaintPartFlags::Grid ); 148 } 149 } 150 151 bool ScDocFunc::AdjustRowHeight( const ScRange& rRange, bool bPaint, bool bApi ) 152 { 153 ScDocument& rDoc = rDocShell.GetDocument(); 154 SfxViewShell* pSomeViewForThisDoc = rDocShell.GetBestViewShell(false); 155 if ( rDoc.IsImportingXML() ) 156 { 157 // for XML import, all row heights are updated together after importing 158 return false; 159 } 160 if ( rDoc.IsAdjustHeightLocked() ) 161 { 162 return false; 163 } 164 165 SCTAB nTab = rRange.aStart.Tab(); 166 SCROW nStartRow = rRange.aStart.Row(); 167 SCROW nEndRow = rRange.aEnd.Row(); 168 169 ScSizeDeviceProvider aProv( &rDocShell ); 170 Fraction aOne(1,1); 171 172 sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice()); 173 bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi); 174 // tdf#76183: recalculate objects' positions 175 if (bChanged) 176 { 177 if (comphelper::LibreOfficeKit::isActive()) 178 { 179 SfxViewShell* pViewShell = SfxViewShell::GetFirst(); 180 while (pViewShell) 181 { 182 ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(pViewShell); 183 if (pTabViewShell && pTabViewShell->GetDocId() == pSomeViewForThisDoc->GetDocId()) 184 { 185 if (ScPositionHelper* pPosHelper = pTabViewShell->GetViewData().GetLOKHeightHelper(nTab)) 186 pPosHelper->invalidateByIndex(nStartRow); 187 } 188 pViewShell = SfxViewShell::GetNext(*pViewShell); 189 } 190 } 191 rDoc.SetDrawPageSize(nTab); 192 } 193 194 if ( bPaint && bChanged ) 195 rDocShell.PostPaint(ScRange(0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab), 196 PaintPartFlags::Grid | PaintPartFlags::Left); 197 198 if (comphelper::LibreOfficeKit::isActive()) 199 { 200 ScTabViewShell::notifyAllViewsHeaderInvalidation(pSomeViewForThisDoc, ROW_HEADER, nTab); 201 ScTabViewShell::notifyAllViewsSheetGeomInvalidation( 202 pSomeViewForThisDoc, false /* bColumns */, true /* bRows */, true /* bSizes*/, 203 false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab); 204 } 205 206 return bChanged; 207 } 208 209 bool ScDocFunc::DetectiveAddPred(const ScAddress& rPos) 210 { 211 ScDocShellModificator aModificator( rDocShell ); 212 213 rDocShell.MakeDrawLayer(); 214 ScDocument& rDoc = rDocShell.GetDocument(); 215 bool bUndo (rDoc.IsUndoEnabled()); 216 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 217 SCCOL nCol = rPos.Col(); 218 SCROW nRow = rPos.Row(); 219 SCTAB nTab = rPos.Tab(); 220 221 if (bUndo) 222 pModel->BeginCalcUndo(false); 223 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowPred( nCol, nRow ); 224 std::unique_ptr<SdrUndoGroup> pUndo; 225 if (bUndo) 226 pUndo = pModel->GetCalcUndo(); 227 if (bDone) 228 { 229 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDPRED ); 230 rDoc.AddDetectiveOperation( aOperation ); 231 if (bUndo) 232 { 233 rDocShell.GetUndoManager()->AddUndoAction( 234 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) ); 235 } 236 aModificator.SetDocumentModified(); 237 SfxBindings* pBindings = rDocShell.GetViewBindings(); 238 if (pBindings) 239 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 240 } 241 242 return bDone; 243 } 244 245 bool ScDocFunc::DetectiveDelPred(const ScAddress& rPos) 246 { 247 ScDocument& rDoc = rDocShell.GetDocument(); 248 249 bool bUndo(rDoc.IsUndoEnabled()); 250 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 251 if (!pModel) 252 return false; 253 254 ScDocShellModificator aModificator( rDocShell ); 255 256 SCCOL nCol = rPos.Col(); 257 SCROW nRow = rPos.Row(); 258 SCTAB nTab = rPos.Tab(); 259 260 if (bUndo) 261 pModel->BeginCalcUndo(false); 262 bool bDone = ScDetectiveFunc(rDoc, nTab).DeletePred( nCol, nRow ); 263 std::unique_ptr<SdrUndoGroup> pUndo; 264 if (bUndo) 265 pUndo = pModel->GetCalcUndo(); 266 if (bDone) 267 { 268 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELPRED ); 269 rDoc.AddDetectiveOperation( aOperation ); 270 if (bUndo) 271 { 272 rDocShell.GetUndoManager()->AddUndoAction( 273 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) ); 274 } 275 aModificator.SetDocumentModified(); 276 SfxBindings* pBindings = rDocShell.GetViewBindings(); 277 if (pBindings) 278 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 279 } 280 281 return bDone; 282 } 283 284 bool ScDocFunc::DetectiveAddSucc(const ScAddress& rPos) 285 { 286 ScDocShellModificator aModificator( rDocShell ); 287 288 rDocShell.MakeDrawLayer(); 289 ScDocument& rDoc = rDocShell.GetDocument(); 290 291 bool bUndo(rDoc.IsUndoEnabled()); 292 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 293 SCCOL nCol = rPos.Col(); 294 SCROW nRow = rPos.Row(); 295 SCTAB nTab = rPos.Tab(); 296 297 if (bUndo) 298 pModel->BeginCalcUndo(false); 299 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowSucc( nCol, nRow ); 300 std::unique_ptr<SdrUndoGroup> pUndo; 301 if (bUndo) 302 pUndo = pModel->GetCalcUndo(); 303 if (bDone) 304 { 305 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDSUCC ); 306 rDoc.AddDetectiveOperation( aOperation ); 307 if (bUndo) 308 { 309 rDocShell.GetUndoManager()->AddUndoAction( 310 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) ); 311 } 312 aModificator.SetDocumentModified(); 313 SfxBindings* pBindings = rDocShell.GetViewBindings(); 314 if (pBindings) 315 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 316 } 317 318 return bDone; 319 } 320 321 bool ScDocFunc::DetectiveDelSucc(const ScAddress& rPos) 322 { 323 ScDocument& rDoc = rDocShell.GetDocument(); 324 325 bool bUndo (rDoc.IsUndoEnabled()); 326 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 327 if (!pModel) 328 return false; 329 330 ScDocShellModificator aModificator( rDocShell ); 331 332 SCCOL nCol = rPos.Col(); 333 SCROW nRow = rPos.Row(); 334 SCTAB nTab = rPos.Tab(); 335 336 if (bUndo) 337 pModel->BeginCalcUndo(false); 338 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteSucc( nCol, nRow ); 339 std::unique_ptr<SdrUndoGroup> pUndo; 340 if (bUndo) 341 pUndo = pModel->GetCalcUndo(); 342 if (bDone) 343 { 344 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_DELSUCC ); 345 rDoc.AddDetectiveOperation( aOperation ); 346 if (bUndo) 347 { 348 rDocShell.GetUndoManager()->AddUndoAction( 349 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) ); 350 } 351 aModificator.SetDocumentModified(); 352 SfxBindings* pBindings = rDocShell.GetViewBindings(); 353 if (pBindings) 354 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 355 } 356 357 return bDone; 358 } 359 360 bool ScDocFunc::DetectiveAddError(const ScAddress& rPos) 361 { 362 ScDocShellModificator aModificator( rDocShell ); 363 364 rDocShell.MakeDrawLayer(); 365 ScDocument& rDoc = rDocShell.GetDocument(); 366 367 bool bUndo (rDoc.IsUndoEnabled()); 368 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 369 SCCOL nCol = rPos.Col(); 370 SCROW nRow = rPos.Row(); 371 SCTAB nTab = rPos.Tab(); 372 373 if (bUndo) 374 pModel->BeginCalcUndo(false); 375 bool bDone = ScDetectiveFunc(rDoc, nTab).ShowError( nCol, nRow ); 376 std::unique_ptr<SdrUndoGroup> pUndo; 377 if (bUndo) 378 pUndo = pModel->GetCalcUndo(); 379 if (bDone) 380 { 381 ScDetOpData aOperation( ScAddress(nCol,nRow,nTab), SCDETOP_ADDERROR ); 382 rDoc.AddDetectiveOperation( aOperation ); 383 if (bUndo) 384 { 385 rDocShell.GetUndoManager()->AddUndoAction( 386 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), &aOperation ) ); 387 } 388 aModificator.SetDocumentModified(); 389 SfxBindings* pBindings = rDocShell.GetViewBindings(); 390 if (pBindings) 391 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 392 } 393 394 return bDone; 395 } 396 397 bool ScDocFunc::DetectiveMarkInvalid(SCTAB nTab) 398 { 399 ScDocShellModificator aModificator( rDocShell ); 400 401 rDocShell.MakeDrawLayer(); 402 ScDocument& rDoc = rDocShell.GetDocument(); 403 404 bool bUndo (rDoc.IsUndoEnabled()); 405 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 406 407 std::unique_ptr<weld::WaitObject> xWaitWin(new weld::WaitObject(ScDocShell::GetActiveDialogParent())); 408 if (bUndo) 409 pModel->BeginCalcUndo(false); 410 bool bOverflow; 411 bool bDone = ScDetectiveFunc(rDoc, nTab).MarkInvalid( bOverflow ); 412 std::unique_ptr<SdrUndoGroup> pUndo; 413 if (bUndo) 414 pUndo = pModel->GetCalcUndo(); 415 xWaitWin.reset(); 416 if (bDone) 417 { 418 if (pUndo && bUndo) 419 { 420 pUndo->SetComment( ScResId( STR_UNDO_DETINVALID ) ); 421 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndo) ); 422 } 423 aModificator.SetDocumentModified(); 424 if ( bOverflow ) 425 { 426 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, 427 VclMessageType::Info, VclButtonsType::Ok, 428 ScResId(STR_DETINVALID_OVERFLOW))); 429 xInfoBox->run(); 430 } 431 } 432 433 return bDone; 434 } 435 436 bool ScDocFunc::DetectiveDelAll(SCTAB nTab) 437 { 438 ScDocument& rDoc = rDocShell.GetDocument(); 439 440 bool bUndo (rDoc.IsUndoEnabled()); 441 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 442 if (!pModel) 443 return false; 444 445 ScDocShellModificator aModificator( rDocShell ); 446 447 if (bUndo) 448 pModel->BeginCalcUndo(false); 449 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Detective ); 450 std::unique_ptr<SdrUndoGroup> pUndo; 451 if (bUndo) 452 pUndo = pModel->GetCalcUndo(); 453 if (bDone) 454 { 455 ScDetOpList* pOldList = rDoc.GetDetOpList(); 456 std::unique_ptr<ScDetOpList> pUndoList; 457 if (bUndo && pOldList) 458 pUndoList.reset(new ScDetOpList(*pOldList)); 459 460 rDoc.ClearDetectiveOperations(); 461 462 if (bUndo) 463 { 464 rDocShell.GetUndoManager()->AddUndoAction( 465 std::make_unique<ScUndoDetective>( &rDocShell, std::move(pUndo), nullptr, std::move(pUndoList) ) ); 466 } 467 aModificator.SetDocumentModified(); 468 SfxBindings* pBindings = rDocShell.GetViewBindings(); 469 if (pBindings) 470 pBindings->Invalidate( SID_DETECTIVE_REFRESH ); 471 } 472 473 return bDone; 474 } 475 476 bool ScDocFunc::DetectiveRefresh( bool bAutomatic ) 477 { 478 bool bDone = false; 479 ScDocument& rDoc = rDocShell.GetDocument(); 480 481 ScDetOpList* pList = rDoc.GetDetOpList(); 482 if ( pList && pList->Count() ) 483 { 484 rDocShell.MakeDrawLayer(); 485 ScDrawLayer* pModel = rDoc.GetDrawLayer(); 486 const bool bUndo (rDoc.IsUndoEnabled()); 487 if (bUndo) 488 pModel->BeginCalcUndo(false); 489 490 // Delete in all sheets 491 492 SCTAB nTabCount = rDoc.GetTableCount(); 493 for (SCTAB nTab=0; nTab<nTabCount; nTab++) 494 ScDetectiveFunc( rDoc,nTab ).DeleteAll( ScDetectiveDelete::Arrows ); // don't remove circles 495 496 // repeat 497 498 size_t nCount = pList->Count(); 499 for (size_t i=0; i < nCount; ++i) 500 { 501 const ScDetOpData& rData = pList->GetObject(i); 502 const ScAddress& aPos = rData.GetPos(); 503 ScDetectiveFunc aFunc( rDoc, aPos.Tab() ); 504 SCCOL nCol = aPos.Col(); 505 SCROW nRow = aPos.Row(); 506 switch (rData.GetOperation()) 507 { 508 case SCDETOP_ADDSUCC: 509 aFunc.ShowSucc( nCol, nRow ); 510 break; 511 case SCDETOP_DELSUCC: 512 aFunc.DeleteSucc( nCol, nRow ); 513 break; 514 case SCDETOP_ADDPRED: 515 aFunc.ShowPred( nCol, nRow ); 516 break; 517 case SCDETOP_DELPRED: 518 aFunc.DeletePred( nCol, nRow ); 519 break; 520 case SCDETOP_ADDERROR: 521 aFunc.ShowError( nCol, nRow ); 522 break; 523 default: 524 OSL_FAIL("wrong operation in DetectiveRefresh"); 525 } 526 } 527 528 if (bUndo) 529 { 530 std::unique_ptr<SdrUndoGroup> pUndo = pModel->GetCalcUndo(); 531 if (pUndo) 532 { 533 pUndo->SetComment( ScResId( STR_UNDO_DETREFRESH ) ); 534 // associate with the last action 535 rDocShell.GetUndoManager()->AddUndoAction( 536 std::make_unique<ScUndoDraw>( std::move(pUndo), &rDocShell ), 537 bAutomatic ); 538 } 539 } 540 rDocShell.SetDrawModified(); 541 bDone = true; 542 } 543 return bDone; 544 } 545 546 static void lcl_collectAllPredOrSuccRanges( 547 const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens, ScDocShell& rDocShell, 548 bool bPred) 549 { 550 ScDocument& rDoc = rDocShell.GetDocument(); 551 vector<ScTokenRef> aRefTokens; 552 if (rSrcRanges.empty()) 553 return; 554 ScRange const & rFrontRange = rSrcRanges.front(); 555 ScDetectiveFunc aDetFunc(rDoc, rFrontRange.aStart.Tab()); 556 for (size_t i = 0, n = rSrcRanges.size(); i < n; ++i) 557 { 558 ScRange const & r = rSrcRanges[i]; 559 if (bPred) 560 { 561 aDetFunc.GetAllPreds( 562 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens); 563 } 564 else 565 { 566 aDetFunc.GetAllSuccs( 567 r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), aRefTokens); 568 } 569 } 570 rRefTokens.swap(aRefTokens); 571 } 572 573 void ScDocFunc::DetectiveCollectAllPreds(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens) 574 { 575 lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, true); 576 } 577 578 void ScDocFunc::DetectiveCollectAllSuccs(const ScRangeList& rSrcRanges, vector<ScTokenRef>& rRefTokens) 579 { 580 lcl_collectAllPredOrSuccRanges(rSrcRanges, rRefTokens, rDocShell, false); 581 } 582 583 bool ScDocFunc::DeleteContents( 584 const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi ) 585 { 586 ScDocShellModificator aModificator( rDocShell ); 587 588 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) 589 { 590 OSL_FAIL("ScDocFunc::DeleteContents without markings"); 591 return false; 592 } 593 594 ScDocument& rDoc = rDocShell.GetDocument(); 595 596 if (bRecord && !rDoc.IsUndoEnabled()) 597 bRecord = false; 598 599 ScEditableTester aTester( rDoc, rMark ); 600 if (!aTester.IsEditable()) 601 { 602 if (!bApi) 603 rDocShell.ErrorMessage(aTester.GetMessageId()); 604 return false; 605 } 606 607 ScMarkData aMultiMark = rMark; 608 aMultiMark.SetMarking(false); // for MarkToMulti 609 610 ScDocumentUniquePtr pUndoDoc; 611 bool bMulti = aMultiMark.IsMultiMarked(); 612 aMultiMark.MarkToMulti(); 613 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea(); 614 ScRange aExtendedRange(aMarkRange); 615 if ( rDoc.ExtendMerge( aExtendedRange, true ) ) 616 bMulti = false; 617 618 // no objects on protected tabs 619 bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark); 620 621 sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted 622 if ( nFlags & InsertDeleteFlags::ATTRIB ) 623 rDocShell.UpdatePaintExt( nExtFlags, aMarkRange ); 624 625 // order of operations: 626 // 1) BeginDrawUndo 627 // 2) Delete objects (DrawUndo will be filled) 628 // 3) Copy content for undo and set up undo actions 629 // 4) Delete content 630 631 bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); 632 if (bRecord && bDrawUndo) 633 rDoc.BeginDrawUndo(); 634 635 if (bObjects) 636 { 637 if (bMulti) 638 rDoc.DeleteObjectsInSelection( aMultiMark ); 639 else 640 rDoc.DeleteObjectsInArea( aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), 641 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), 642 aMultiMark ); 643 } 644 645 // To keep track of all non-empty cells within the deleted area. 646 std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans; 647 648 if ( bRecord ) 649 { 650 pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, aMultiMark, aMarkRange, nFlags, bMulti); 651 pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, aMultiMark, aMarkRange); 652 } 653 654 rDoc.DeleteSelection( nFlags, aMultiMark ); 655 656 // add undo action after drawing undo is complete (objects and note captions) 657 if( bRecord ) 658 { 659 sc::DocFuncUtil::addDeleteContentsUndo( 660 rDocShell.GetUndoManager(), &rDocShell, aMultiMark, aExtendedRange, 661 std::move(pUndoDoc), nFlags, pDataSpans, bMulti, bDrawUndo); 662 } 663 664 if (!AdjustRowHeight( aExtendedRange, true, bApi )) 665 rDocShell.PostPaint( aExtendedRange, PaintPartFlags::Grid, nExtFlags ); 666 else if (nExtFlags & SC_PF_LINES) 667 lcl_PaintAbove( rDocShell, aExtendedRange ); // for lines above the range 668 669 aModificator.SetDocumentModified(); 670 671 return true; 672 } 673 674 bool ScDocFunc::DeleteCell( 675 const ScAddress& rPos, const ScMarkData& rMark, InsertDeleteFlags nFlags, bool bRecord, bool bApi ) 676 { 677 ScDocShellModificator aModificator(rDocShell); 678 679 ScDocument& rDoc = rDocShell.GetDocument(); 680 681 if (bRecord && !rDoc.IsUndoEnabled()) 682 bRecord = false; 683 684 ScEditableTester aTester(rDoc, rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark); 685 if (!aTester.IsEditable()) 686 { 687 rDocShell.ErrorMessage(aTester.GetMessageId()); 688 return false; 689 } 690 691 // no objects on protected tabs 692 bool bObjects = (nFlags & InsertDeleteFlags::OBJECTS) && !sc::DocFuncUtil::hasProtectedTab(rDoc, rMark); 693 694 sal_uInt16 nExtFlags = 0; // extra flags are needed only if attributes are deleted 695 if (nFlags & InsertDeleteFlags::ATTRIB) 696 rDocShell.UpdatePaintExt(nExtFlags, rPos); 697 698 // order of operations: 699 // 1) BeginDrawUndo 700 // 2) delete objects (DrawUndo is filled) 701 // 3) copy contents for undo 702 // 4) delete contents 703 // 5) add undo-action 704 705 bool bDrawUndo = bObjects || (nFlags & InsertDeleteFlags::NOTE); // needed for shown notes 706 if (bDrawUndo && bRecord) 707 rDoc.BeginDrawUndo(); 708 709 if (bObjects) 710 rDoc.DeleteObjectsInArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark); 711 712 // To keep track of all non-empty cells within the deleted area. 713 std::shared_ptr<ScSimpleUndo::DataSpansType> pDataSpans; 714 715 ScDocumentUniquePtr pUndoDoc; 716 if (bRecord) 717 { 718 pUndoDoc = sc::DocFuncUtil::createDeleteContentsUndoDoc(rDoc, rMark, rPos, nFlags, false); 719 pDataSpans = sc::DocFuncUtil::getNonEmptyCellSpans(rDoc, rMark, rPos); 720 } 721 722 rDoc.DeleteArea(rPos.Col(), rPos.Row(), rPos.Col(), rPos.Row(), rMark, nFlags); 723 724 if (bRecord) 725 { 726 sc::DocFuncUtil::addDeleteContentsUndo( 727 rDocShell.GetUndoManager(), &rDocShell, rMark, rPos, std::move(pUndoDoc), 728 nFlags, pDataSpans, false, bDrawUndo); 729 } 730 731 if (!AdjustRowHeight(rPos, true, bApi)) 732 rDocShell.PostPaint( 733 rPos.Col(), rPos.Row(), rPos.Tab(), rPos.Col(), rPos.Row(), rPos.Tab(), 734 PaintPartFlags::Grid, nExtFlags); 735 736 aModificator.SetDocumentModified(); 737 738 return true; 739 } 740 741 bool ScDocFunc::TransliterateText( const ScMarkData& rMark, TransliterationFlags nType, 742 bool bApi ) 743 { 744 ScDocShellModificator aModificator( rDocShell ); 745 746 ScDocument& rDoc = rDocShell.GetDocument(); 747 bool bRecord = true; 748 if (!rDoc.IsUndoEnabled()) 749 bRecord = false; 750 751 ScEditableTester aTester( rDoc, rMark ); 752 if (!aTester.IsEditable()) 753 { 754 if (!bApi) 755 rDocShell.ErrorMessage(aTester.GetMessageId()); 756 return false; 757 } 758 759 ScMarkData aMultiMark = rMark; 760 aMultiMark.SetMarking(false); // for MarkToMulti 761 aMultiMark.MarkToMulti(); 762 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea(); 763 764 if (bRecord) 765 { 766 SCTAB nStartTab = aMarkRange.aStart.Tab(); 767 SCTAB nTabCount = rDoc.GetTableCount(); 768 769 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 770 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab ); 771 for (const auto& rTab : rMark) 772 { 773 if (rTab >= nTabCount) 774 break; 775 776 if (rTab != nStartTab) 777 pUndoDoc->AddUndoTab( rTab, rTab ); 778 } 779 780 ScRange aCopyRange = aMarkRange; 781 aCopyRange.aStart.SetTab(0); 782 aCopyRange.aEnd.SetTab(nTabCount-1); 783 rDoc.CopyToDocument(aCopyRange, InsertDeleteFlags::CONTENTS, true, *pUndoDoc, &aMultiMark); 784 785 rDocShell.GetUndoManager()->AddUndoAction( 786 std::make_unique<ScUndoTransliterate>( &rDocShell, aMultiMark, std::move(pUndoDoc), nType ) ); 787 } 788 789 rDoc.TransliterateText( aMultiMark, nType ); 790 791 if (!AdjustRowHeight( aMarkRange, true, true )) 792 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid ); 793 794 aModificator.SetDocumentModified(); 795 796 return true; 797 } 798 799 bool ScDocFunc::SetNormalString( bool& o_rbNumFmtSet, const ScAddress& rPos, const OUString& rText, bool bApi ) 800 { 801 ScDocShellModificator aModificator( rDocShell ); 802 ScDocument& rDoc = rDocShell.GetDocument(); 803 804 bool bUndo(rDoc.IsUndoEnabled()); 805 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() ); 806 if (!aTester.IsEditable()) 807 { 808 if (!bApi) 809 rDocShell.ErrorMessage(aTester.GetMessageId()); 810 return false; 811 } 812 813 bool bEditDeleted = (rDoc.GetCellType(rPos) == CELLTYPE_EDIT); 814 ScUndoEnterData::ValuesType aOldValues; 815 816 if (bUndo) 817 { 818 ScUndoEnterData::Value aOldValue; 819 820 aOldValue.mnTab = rPos.Tab(); 821 aOldValue.maCell.assign(rDoc, rPos); 822 823 const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(),rPos.Row(),rPos.Tab() ); 824 if ( const SfxUInt32Item* pItem = pPattern->GetItemSet().GetItemIfSet( 825 ATTR_VALUE_FORMAT,false) ) 826 { 827 aOldValue.mbHasFormat = true; 828 aOldValue.mnFormat = pItem->GetValue(); 829 } 830 else 831 aOldValue.mbHasFormat = false; 832 833 aOldValues.push_back(aOldValue); 834 } 835 836 o_rbNumFmtSet = rDoc.SetString( rPos.Col(), rPos.Row(), rPos.Tab(), rText ); 837 838 if (bUndo) 839 { 840 // because of ChangeTracking, UndoAction can be created only after SetString was called 841 rDocShell.GetUndoManager()->AddUndoAction( 842 std::make_unique<ScUndoEnterData>(&rDocShell, rPos, aOldValues, rText, nullptr)); 843 } 844 845 if ( bEditDeleted || rDoc.HasAttrib( ScRange(rPos), HasAttrFlags::NeedHeight ) ) 846 AdjustRowHeight( ScRange(rPos), true, bApi ); 847 848 rDocShell.PostPaintCell( rPos ); 849 aModificator.SetDocumentModified(); 850 851 // notify input handler here the same way as in PutCell 852 if (bApi) 853 NotifyInputHandler( rPos ); 854 855 const SfxUInt32Item* pItem = rDoc.GetAttr(rPos, ATTR_VALIDDATA); 856 const ScValidationData* pData = rDoc.GetValidationEntry(pItem->GetValue()); 857 if (pData) 858 { 859 ScRefCellValue aCell(rDoc, rPos); 860 if (pData->IsDataValid(aCell, rPos)) 861 ScDetectiveFunc(rDoc, rPos.Tab()).DeleteCirclesAt(rPos.Col(), rPos.Row()); 862 } 863 864 return true; 865 } 866 867 bool ScDocFunc::SetValueCell( const ScAddress& rPos, double fVal, bool bInteraction ) 868 { 869 ScDocShellModificator aModificator( rDocShell ); 870 ScDocument& rDoc = rDocShell.GetDocument(); 871 bool bUndo = rDoc.IsUndoEnabled(); 872 873 bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight); 874 875 ScCellValue aOldVal; 876 if (bUndo) 877 aOldVal.assign(rDoc, rPos); 878 879 rDoc.SetValue(rPos, fVal); 880 881 if (bUndo) 882 { 883 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 884 ScCellValue aNewVal; 885 aNewVal.assign(rDoc, rPos); 886 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal)); 887 } 888 889 if (bHeight) 890 AdjustRowHeight(rPos, true, !bInteraction); 891 892 rDocShell.PostPaintCell( rPos ); 893 aModificator.SetDocumentModified(); 894 895 // #103934#; notify editline and cell in edit mode 896 if (!bInteraction) 897 NotifyInputHandler( rPos ); 898 899 return true; 900 } 901 902 void ScDocFunc::SetValueCells( const ScAddress& rPos, const std::vector<double>& aVals, bool bInteraction ) 903 { 904 ScDocument& rDoc = rDocShell.GetDocument(); 905 906 // Check for invalid range. 907 SCROW nLastRow = rPos.Row() + aVals.size() - 1; 908 if (nLastRow > rDoc.MaxRow()) 909 // out of bound. 910 return; 911 912 ScRange aRange(rPos); 913 aRange.aEnd.SetRow(nLastRow); 914 915 ScDocShellModificator aModificator(rDocShell); 916 917 if (rDoc.IsUndoEnabled()) 918 { 919 std::unique_ptr<sc::UndoSetCells> pUndoObj(new sc::UndoSetCells(&rDocShell, rPos)); 920 rDoc.TransferCellValuesTo(rPos, aVals.size(), pUndoObj->GetOldValues()); 921 pUndoObj->SetNewValues(aVals); 922 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 923 pUndoMgr->AddUndoAction(std::move(pUndoObj)); 924 } 925 926 rDoc.SetValues(rPos, aVals); 927 928 rDocShell.PostPaint(aRange, PaintPartFlags::Grid); 929 aModificator.SetDocumentModified(); 930 931 // #103934#; notify editline and cell in edit mode 932 if (!bInteraction) 933 NotifyInputHandler(rPos); 934 } 935 936 bool ScDocFunc::SetStringCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction ) 937 { 938 ScDocShellModificator aModificator( rDocShell ); 939 ScDocument& rDoc = rDocShell.GetDocument(); 940 bool bUndo = rDoc.IsUndoEnabled(); 941 942 bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight); 943 944 ScCellValue aOldVal; 945 if (bUndo) 946 aOldVal.assign(rDoc, rPos); 947 948 ScSetStringParam aParam; 949 aParam.setTextInput(); 950 rDoc.SetString(rPos, rStr, &aParam); 951 952 if (bUndo) 953 { 954 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 955 ScCellValue aNewVal; 956 aNewVal.assign(rDoc, rPos); 957 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal)); 958 } 959 960 if (bHeight) 961 AdjustRowHeight(rPos, true, !bInteraction); 962 963 rDocShell.PostPaintCell( rPos ); 964 aModificator.SetDocumentModified(); 965 966 // #103934#; notify editline and cell in edit mode 967 if (!bInteraction) 968 NotifyInputHandler( rPos ); 969 970 return true; 971 } 972 973 bool ScDocFunc::SetEditCell( const ScAddress& rPos, const EditTextObject& rStr, bool bInteraction ) 974 { 975 ScDocShellModificator aModificator( rDocShell ); 976 ScDocument& rDoc = rDocShell.GetDocument(); 977 bool bUndo = rDoc.IsUndoEnabled(); 978 979 bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight); 980 981 ScCellValue aOldVal; 982 if (bUndo) 983 aOldVal.assign(rDoc, rPos); 984 985 rDoc.SetEditText(rPos, rStr.Clone()); 986 987 if (bUndo) 988 { 989 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 990 ScCellValue aNewVal; 991 aNewVal.assign(rDoc, rPos); 992 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal)); 993 } 994 995 if (bHeight) 996 AdjustRowHeight(rPos, true, !bInteraction); 997 998 rDocShell.PostPaintCell( rPos ); 999 aModificator.SetDocumentModified(); 1000 1001 // #103934#; notify editline and cell in edit mode 1002 if (!bInteraction) 1003 NotifyInputHandler( rPos ); 1004 1005 return true; 1006 } 1007 1008 bool ScDocFunc::SetStringOrEditCell( const ScAddress& rPos, const OUString& rStr, bool bInteraction ) 1009 { 1010 ScDocument& rDoc = rDocShell.GetDocument(); 1011 1012 if (ScStringUtil::isMultiline(rStr)) 1013 { 1014 ScFieldEditEngine& rEngine = rDoc.GetEditEngine(); 1015 rEngine.SetTextCurrentDefaults(rStr); 1016 std::unique_ptr<EditTextObject> pEditText(rEngine.CreateTextObject()); 1017 return SetEditCell(rPos, *pEditText, bInteraction); 1018 } 1019 else 1020 return SetStringCell(rPos, rStr, bInteraction); 1021 } 1022 1023 bool ScDocFunc::SetFormulaCell( const ScAddress& rPos, ScFormulaCell* pCell, bool bInteraction ) 1024 { 1025 std::unique_ptr<ScFormulaCell> xCell(pCell); 1026 1027 ScDocShellModificator aModificator( rDocShell ); 1028 ScDocument& rDoc = rDocShell.GetDocument(); 1029 bool bUndo = rDoc.IsUndoEnabled(); 1030 1031 bool bHeight = rDoc.HasAttrib(rPos, HasAttrFlags::NeedHeight); 1032 1033 ScCellValue aOldVal; 1034 if (bUndo) 1035 aOldVal.assign(rDoc, rPos); 1036 1037 pCell = rDoc.SetFormulaCell(rPos, xCell.release()); 1038 1039 // For performance reasons API calls may disable calculation while 1040 // operating and recalculate once when done. If through user interaction 1041 // and AutoCalc is disabled, calculate the formula (without its 1042 // dependencies) once so the result matches the current document's content. 1043 if (bInteraction && !rDoc.GetAutoCalc() && pCell) 1044 { 1045 // calculate just the cell once and set Dirty again 1046 pCell->Interpret(); 1047 pCell->SetDirtyVar(); 1048 rDoc.PutInFormulaTree( pCell); 1049 } 1050 1051 if (bUndo) 1052 { 1053 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 1054 ScCellValue aNewVal; 1055 aNewVal.assign(rDoc, rPos); 1056 pUndoMgr->AddUndoAction(std::make_unique<ScUndoSetCell>(&rDocShell, rPos, aOldVal, aNewVal)); 1057 } 1058 1059 if (bHeight) 1060 AdjustRowHeight(rPos, true, !bInteraction); 1061 1062 rDocShell.PostPaintCell( rPos ); 1063 aModificator.SetDocumentModified(); 1064 1065 // #103934#; notify editline and cell in edit mode 1066 if (!bInteraction) 1067 NotifyInputHandler( rPos ); 1068 1069 return true; 1070 } 1071 1072 bool ScDocFunc::SetFormulaCells( const ScAddress& rPos, std::vector<ScFormulaCell*>& rCells, bool bInteraction ) 1073 { 1074 ScDocument& rDoc = rDocShell.GetDocument(); 1075 1076 const size_t nLength = rCells.size(); 1077 if (rPos.Row() + nLength - 1 > o3tl::make_unsigned(rDoc.MaxRow())) 1078 // out of bound 1079 return false; 1080 1081 ScRange aRange(rPos); 1082 aRange.aEnd.IncRow(nLength - 1); 1083 1084 ScDocShellModificator aModificator( rDocShell ); 1085 bool bUndo = rDoc.IsUndoEnabled(); 1086 1087 std::unique_ptr<sc::UndoSetCells> pUndoObj; 1088 if (bUndo) 1089 { 1090 pUndoObj.reset(new sc::UndoSetCells(&rDocShell, rPos)); 1091 rDoc.TransferCellValuesTo(rPos, nLength, pUndoObj->GetOldValues()); 1092 } 1093 1094 rDoc.SetFormulaCells(rPos, rCells); 1095 1096 // For performance reasons API calls may disable calculation while 1097 // operating and recalculate once when done. If through user interaction 1098 // and AutoCalc is disabled, calculate the formula (without its 1099 // dependencies) once so the result matches the current document's content. 1100 if (bInteraction && !rDoc.GetAutoCalc()) 1101 { 1102 for (auto* pCell : rCells) 1103 { 1104 // calculate just the cell once and set Dirty again 1105 pCell->Interpret(); 1106 pCell->SetDirtyVar(); 1107 rDoc.PutInFormulaTree( pCell); 1108 } 1109 } 1110 1111 if (bUndo) 1112 { 1113 pUndoObj->SetNewValues(rCells); 1114 SfxUndoManager* pUndoMgr = rDocShell.GetUndoManager(); 1115 pUndoMgr->AddUndoAction(std::move(pUndoObj)); 1116 } 1117 1118 rDocShell.PostPaint(aRange, PaintPartFlags::Grid); 1119 aModificator.SetDocumentModified(); 1120 1121 // #103934#; notify editline and cell in edit mode 1122 if (!bInteraction) 1123 NotifyInputHandler( rPos ); 1124 1125 return true; 1126 } 1127 1128 void ScDocFunc::NotifyInputHandler( const ScAddress& rPos ) 1129 { 1130 ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell(); 1131 if ( !(pViewSh && pViewSh->GetViewData().GetDocShell() == &rDocShell) ) 1132 return; 1133 1134 ScInputHandler* pInputHdl = SC_MOD()->GetInputHdl(); 1135 if ( pInputHdl && pInputHdl->GetCursorPos() == rPos ) 1136 { 1137 bool bIsEditMode(pInputHdl->IsEditMode()); 1138 1139 // set modified if in editmode, because so the string is not set in the InputWindow like in the cell 1140 // (the cell shows the same like the InputWindow) 1141 if (bIsEditMode) 1142 pInputHdl->SetModified(); 1143 pViewSh->UpdateInputHandler(false, !bIsEditMode); 1144 } 1145 } 1146 1147 namespace { 1148 1149 struct ScMyRememberItem 1150 { 1151 sal_Int32 nIndex; 1152 SfxItemSet aItemSet; 1153 1154 ScMyRememberItem(SfxItemSet _aItemSet, sal_Int32 nTempIndex) : 1155 nIndex(nTempIndex), aItemSet(std::move(_aItemSet)) {} 1156 }; 1157 1158 } 1159 1160 void ScDocFunc::PutData( const ScAddress& rPos, ScEditEngineDefaulter& rEngine, bool bApi ) 1161 { 1162 // PutData calls PutCell or SetNormalString 1163 1164 bool bRet = false; 1165 ScDocument& rDoc = rDocShell.GetDocument(); 1166 ScEditAttrTester aTester( &rEngine ); 1167 bool bEditCell = aTester.NeedsObject(); 1168 if ( bEditCell ) 1169 { 1170 // #i61702# With bLoseContent set, the content of rEngine isn't restored 1171 // (used in loading XML, where after the removeActionLock call the API object's 1172 // EditEngine isn't accessed again. 1173 bool bLoseContent = rDoc.IsImportingXML(); 1174 1175 const bool bUpdateMode = rEngine.SetUpdateLayout(false); 1176 1177 std::vector<std::unique_ptr<ScMyRememberItem>> aRememberItems; 1178 1179 // All paragraph attributes must be removed before calling CreateTextObject, 1180 // not only alignment, so the object doesn't contain the cell attributes as 1181 // paragraph attributes. Before removing the attributes store them in a vector to 1182 // set them back to the EditEngine. 1183 sal_Int32 nCount = rEngine.GetParagraphCount(); 1184 for (sal_Int32 i=0; i<nCount; i++) 1185 { 1186 const SfxItemSet& rOld = rEngine.GetParaAttribs( i ); 1187 if ( rOld.Count() ) 1188 { 1189 if ( !bLoseContent ) 1190 { 1191 aRememberItems.push_back(std::make_unique<ScMyRememberItem>(rEngine.GetParaAttribs(i), i)); 1192 } 1193 rEngine.SetParaAttribs( i, SfxItemSet( *rOld.GetPool(), rOld.GetRanges() ) ); 1194 } 1195 } 1196 1197 // A copy of pNewData will be stored in the cell. 1198 std::unique_ptr<EditTextObject> pNewData(rEngine.CreateTextObject()); 1199 bRet = SetEditCell(rPos, *pNewData, !bApi); 1200 1201 // Set the paragraph attributes back to the EditEngine. 1202 for (const auto& rxItem : aRememberItems) 1203 { 1204 rEngine.SetParaAttribs(rxItem->nIndex, rxItem->aItemSet); 1205 } 1206 1207 // #i61702# if the content isn't accessed, there's no need to set the UpdateMode again 1208 if ( bUpdateMode && !bLoseContent ) 1209 rEngine.SetUpdateLayout(true); 1210 } 1211 else 1212 { 1213 OUString aText = rEngine.GetText(); 1214 if (aText.isEmpty()) 1215 { 1216 bool bNumFmtSet = false; 1217 bRet = SetNormalString( bNumFmtSet, rPos, aText, bApi ); 1218 } 1219 else 1220 bRet = SetStringCell(rPos, aText, !bApi); 1221 } 1222 1223 if ( !(bRet && aTester.NeedsCellAttr()) ) 1224 return; 1225 1226 const SfxItemSet& rEditAttr = aTester.GetAttribs(); 1227 ScPatternAttr aPattern( rDoc.GetPool() ); 1228 aPattern.GetFromEditItemSet( &rEditAttr ); 1229 aPattern.DeleteUnchanged( rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ) ); 1230 aPattern.GetItemSet().ClearItem( ATTR_HOR_JUSTIFY ); // wasn't removed above if no edit object 1231 if ( aPattern.GetItemSet().Count() > 0 ) 1232 { 1233 ScMarkData aMark(rDoc.GetSheetLimits()); 1234 aMark.SelectTable( rPos.Tab(), true ); 1235 aMark.SetMarkArea( ScRange( rPos ) ); 1236 ApplyAttributes( aMark, aPattern, bApi ); 1237 } 1238 } 1239 1240 bool ScDocFunc::SetCellText( 1241 const ScAddress& rPos, const OUString& rText, bool bInterpret, bool bEnglish, bool bApi, 1242 const formula::FormulaGrammar::Grammar eGrammar ) 1243 { 1244 bool bSet = false; 1245 if ( bInterpret ) 1246 { 1247 if ( bEnglish ) 1248 { 1249 ScDocument& rDoc = rDocShell.GetDocument(); 1250 1251 ::std::optional<ScExternalRefManager::ApiGuard> pExtRefGuard; 1252 if (bApi) 1253 pExtRefGuard.emplace(rDoc); 1254 1255 ScInputStringType aRes = 1256 ScStringUtil::parseInputString(*rDoc.GetFormatTable(), rText, LANGUAGE_ENGLISH_US); 1257 1258 switch (aRes.meType) 1259 { 1260 case ScInputStringType::Formula: 1261 bSet = SetFormulaCell(rPos, new ScFormulaCell(rDoc, rPos, aRes.maText, eGrammar), !bApi); 1262 break; 1263 case ScInputStringType::Number: 1264 bSet = SetValueCell(rPos, aRes.mfValue, !bApi); 1265 break; 1266 case ScInputStringType::Text: 1267 bSet = SetStringOrEditCell(rPos, aRes.maText, !bApi); 1268 break; 1269 default: 1270 ; 1271 } 1272 } 1273 // otherwise keep Null -> SetString with local formulas/number formats 1274 } 1275 else if (!rText.isEmpty()) 1276 { 1277 bSet = SetStringOrEditCell(rPos, rText, !bApi); 1278 } 1279 1280 if (!bSet) 1281 { 1282 bool bNumFmtSet = false; 1283 bSet = SetNormalString( bNumFmtSet, rPos, rText, bApi ); 1284 } 1285 return bSet; 1286 } 1287 1288 bool ScDocFunc::ShowNote( const ScAddress& rPos, bool bShow ) 1289 { 1290 ScDocument& rDoc = rDocShell.GetDocument(); 1291 ScPostIt* pNote = rDoc.GetNote( rPos ); 1292 if( !pNote || (bShow == pNote->IsCaptionShown()) || 1293 (comphelper::LibreOfficeKit::isActive() && !comphelper::LibreOfficeKit::isTiledAnnotations()) ) 1294 return false; 1295 1296 // move the caption to internal or hidden layer and create undo action 1297 pNote->ShowCaption( rPos, bShow ); 1298 if( rDoc.IsUndoEnabled() ) 1299 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideNote>( rDocShell, rPos, bShow ) ); 1300 1301 rDoc.SetStreamValid(rPos.Tab(), false); 1302 1303 ScTabView::OnLOKNoteStateChanged(pNote); 1304 1305 if (ScViewData* pViewData = ScDocShell::GetViewData()) 1306 { 1307 if (ScDrawView* pDrawView = pViewData->GetScDrawView()) 1308 pDrawView->SyncForGrid( pNote->GetCaption()); 1309 } 1310 1311 rDocShell.SetDocumentModified(); 1312 1313 return true; 1314 } 1315 1316 void ScDocFunc::SetNoteText( const ScAddress& rPos, const OUString& rText, bool bApi ) 1317 { 1318 ScDocShellModificator aModificator( rDocShell ); 1319 1320 ScDocument& rDoc = rDocShell.GetDocument(); 1321 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() ); 1322 if (!aTester.IsEditable()) 1323 { 1324 if (!bApi) 1325 rDocShell.ErrorMessage(aTester.GetMessageId()); 1326 return; 1327 } 1328 1329 OUString aNewText = convertLineEnd(rText, GetSystemLineEnd()); //! is this necessary ??? 1330 1331 if( ScPostIt* pNote = (!aNewText.isEmpty()) ? rDoc.GetOrCreateNote( rPos ) : rDoc.GetNote(rPos) ) 1332 pNote->SetText( rPos, aNewText ); 1333 1334 //! Undo !!! 1335 1336 rDoc.SetStreamValid(rPos.Tab(), false); 1337 1338 rDocShell.PostPaintCell( rPos ); 1339 aModificator.SetDocumentModified(); 1340 } 1341 1342 void ScDocFunc::ReplaceNote( const ScAddress& rPos, const OUString& rNoteText, const OUString* pAuthor, const OUString* pDate, bool bApi ) 1343 { 1344 ScDocShellModificator aModificator( rDocShell ); 1345 ScDocument& rDoc = rDocShell.GetDocument(); 1346 ScEditableTester aTester( rDoc, rPos.Tab(), rPos.Col(),rPos.Row(), rPos.Col(),rPos.Row() ); 1347 if (aTester.IsEditable()) 1348 { 1349 ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); 1350 SfxUndoManager* pUndoMgr = (pDrawLayer && rDoc.IsUndoEnabled()) ? rDocShell.GetUndoManager() : nullptr; 1351 1352 ScNoteData aOldData; 1353 std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos ); 1354 sal_uInt32 nNoteId = 0; 1355 if( pOldNote ) 1356 { 1357 nNoteId = pOldNote->GetId(); 1358 // ensure existing caption object before draw undo tracking starts 1359 pOldNote->GetOrCreateCaption( rPos ); 1360 // rescue note data for undo 1361 aOldData = pOldNote->GetNoteData(); 1362 } 1363 1364 // collect drawing undo actions for deleting/inserting caption objects 1365 if( pUndoMgr ) 1366 pDrawLayer->BeginCalcUndo(false); 1367 1368 // delete the note (creates drawing undo action for the caption object) 1369 bool hadOldNote(pOldNote); 1370 pOldNote.reset(); 1371 1372 // create new note (creates drawing undo action for the new caption object) 1373 ScNoteData aNewData; 1374 ScPostIt* pNewNote = nullptr; 1375 if( (pNewNote = ScNoteUtil::CreateNoteFromString( rDoc, rPos, rNoteText, false, true, nNoteId )) ) 1376 { 1377 if( pAuthor ) pNewNote->SetAuthor( *pAuthor ); 1378 if( pDate ) pNewNote->SetDate( *pDate ); 1379 1380 // rescue note data for undo 1381 aNewData = pNewNote->GetNoteData(); 1382 } 1383 1384 // create the undo action 1385 if( pUndoMgr && (aOldData.mxCaption || aNewData.mxCaption) ) 1386 pUndoMgr->AddUndoAction( std::make_unique<ScUndoReplaceNote>( rDocShell, rPos, aOldData, aNewData, pDrawLayer->GetCalcUndo() ) ); 1387 1388 // repaint cell (to make note marker visible) 1389 rDocShell.PostPaintCell( rPos ); 1390 1391 rDoc.SetStreamValid(rPos.Tab(), false); 1392 1393 aModificator.SetDocumentModified(); 1394 1395 // Let our LOK clients know about the new/modified note 1396 if (pNewNote) 1397 { 1398 ScDocShell::LOKCommentNotify(hadOldNote ? LOKCommentNotificationType::Modify : LOKCommentNotificationType::Add, 1399 rDoc, rPos, pNewNote); 1400 } 1401 } 1402 else if (!bApi) 1403 { 1404 rDocShell.ErrorMessage(aTester.GetMessageId()); 1405 } 1406 } 1407 1408 void ScDocFunc::ImportNote( const ScAddress& rPos, 1409 std::unique_ptr<GenerateNoteCaption> xGenerator, 1410 const tools::Rectangle& rCaptionRect, bool bShown ) 1411 { 1412 ScDocShellModificator aModificator( rDocShell ); 1413 ScDocument& rDoc = rDocShell.GetDocument(); 1414 1415 std::unique_ptr<ScPostIt> pOldNote = rDoc.ReleaseNote( rPos ); 1416 SAL_WARN_IF(pOldNote, "sc.ui", "imported data has >1 notes on same cell? at pos " << rPos); 1417 1418 // create new note 1419 ScNoteUtil::CreateNoteFromGenerator(rDoc, rPos, std::move(xGenerator), 1420 rCaptionRect, bShown); 1421 1422 rDoc.SetStreamValid(rPos.Tab(), false); 1423 1424 aModificator.SetDocumentModified(); 1425 } 1426 1427 bool ScDocFunc::ApplyAttributes( const ScMarkData& rMark, const ScPatternAttr& rPattern, 1428 bool bApi ) 1429 { 1430 ScDocument& rDoc = rDocShell.GetDocument(); 1431 bool bRecord = true; 1432 if ( !rDoc.IsUndoEnabled() ) 1433 bRecord = false; 1434 1435 bool bImportingXML = rDoc.IsImportingXML(); 1436 // Cell formats can still be set if the range isn't editable only because of matrix formulas. 1437 // #i62483# When loading XML, the check can be skipped altogether. 1438 bool bOnlyNotBecauseOfMatrix; 1439 if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix ) 1440 && !bOnlyNotBecauseOfMatrix ) 1441 { 1442 if (!bApi) 1443 rDocShell.ErrorMessage(STR_PROTECTIONERR); 1444 return false; 1445 } 1446 1447 ScDocShellModificator aModificator( rDocShell ); 1448 1449 //! Border 1450 1451 ScRange aMultiRange; 1452 bool bMulti = rMark.IsMultiMarked(); 1453 if ( bMulti ) 1454 aMultiRange = rMark.GetMultiMarkArea(); 1455 else 1456 aMultiRange = rMark.GetMarkArea(); 1457 1458 if ( bRecord ) 1459 { 1460 ScDocumentUniquePtr pUndoDoc( new ScDocument( SCDOCMODE_UNDO )); 1461 pUndoDoc->InitUndo( rDoc, aMultiRange.aStart.Tab(), aMultiRange.aEnd.Tab() ); 1462 rDoc.CopyToDocument(aMultiRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark); 1463 1464 rDocShell.GetUndoManager()->AddUndoAction( 1465 std::make_unique<ScUndoSelectionAttr>( 1466 &rDocShell, rMark, 1467 aMultiRange.aStart.Col(), aMultiRange.aStart.Row(), aMultiRange.aStart.Tab(), 1468 aMultiRange.aEnd.Col(), aMultiRange.aEnd.Row(), aMultiRange.aEnd.Tab(), 1469 std::move(pUndoDoc), bMulti, &rPattern ) ); 1470 } 1471 1472 // While loading XML it is not necessary to ask HasAttrib. It needs too much time. 1473 sal_uInt16 nExtFlags = 0; 1474 if ( !bImportingXML ) 1475 rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content before the change 1476 1477 bool bChanged = false; 1478 rDoc.ApplySelectionPattern( rPattern, rMark, nullptr, &bChanged ); 1479 1480 if(bChanged) 1481 { 1482 if ( !bImportingXML ) 1483 rDocShell.UpdatePaintExt( nExtFlags, aMultiRange ); // content after the change 1484 1485 if (!AdjustRowHeight( aMultiRange, true, bApi )) 1486 rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid, nExtFlags ); 1487 else if (nExtFlags & SC_PF_LINES) 1488 lcl_PaintAbove( rDocShell, aMultiRange ); // because of lines above the range 1489 1490 aModificator.SetDocumentModified(); 1491 } 1492 1493 return true; 1494 } 1495 1496 bool ScDocFunc::ApplyStyle( const ScMarkData& rMark, const OUString& rStyleName, 1497 bool bApi ) 1498 { 1499 ScDocument& rDoc = rDocShell.GetDocument(); 1500 bool bRecord = true; 1501 if ( !rDoc.IsUndoEnabled() ) 1502 bRecord = false; 1503 1504 bool bImportingXML = rDoc.IsImportingXML(); 1505 // Cell formats can still be set if the range isn't editable only because of matrix formulas. 1506 // #i62483# When loading XML, the check can be skipped altogether. 1507 bool bOnlyNotBecauseOfMatrix; 1508 if ( !bImportingXML && !rDoc.IsSelectionEditable( rMark, &bOnlyNotBecauseOfMatrix ) 1509 && !bOnlyNotBecauseOfMatrix ) 1510 { 1511 if (!bApi) 1512 rDocShell.ErrorMessage(STR_PROTECTIONERR); 1513 return false; 1514 } 1515 1516 ScStyleSheet* pStyleSheet = static_cast<ScStyleSheet*>( rDoc.GetStyleSheetPool()->Find( 1517 rStyleName, SfxStyleFamily::Para )); 1518 if (!pStyleSheet) 1519 return false; 1520 1521 ScDocShellModificator aModificator( rDocShell ); 1522 1523 ScRange aMultiRange; 1524 bool bMulti = rMark.IsMultiMarked(); 1525 if ( bMulti ) 1526 aMultiRange = rMark.GetMultiMarkArea(); 1527 else 1528 aMultiRange = rMark.GetMarkArea(); 1529 1530 if ( bRecord ) 1531 { 1532 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 1533 SCTAB nStartTab = aMultiRange.aStart.Tab(); 1534 SCTAB nTabCount = rDoc.GetTableCount(); 1535 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab ); 1536 for (const auto& rTab : rMark) 1537 { 1538 if (rTab >= nTabCount) 1539 break; 1540 1541 if (rTab != nStartTab) 1542 pUndoDoc->AddUndoTab( rTab, rTab ); 1543 } 1544 1545 ScRange aCopyRange = aMultiRange; 1546 aCopyRange.aStart.SetTab(0); 1547 aCopyRange.aEnd.SetTab(nTabCount-1); 1548 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, bMulti, *pUndoDoc, &rMark ); 1549 1550 rDocShell.GetUndoManager()->AddUndoAction( 1551 std::make_unique<ScUndoSelectionStyle>( 1552 &rDocShell, rMark, aMultiRange, rStyleName, std::move(pUndoDoc) ) ); 1553 1554 } 1555 1556 rDoc.ApplySelectionStyle( *pStyleSheet, rMark ); 1557 1558 if (!AdjustRowHeight( aMultiRange, true, bApi )) 1559 rDocShell.PostPaint( aMultiRange, PaintPartFlags::Grid ); 1560 1561 aModificator.SetDocumentModified(); 1562 1563 return true; 1564 } 1565 1566 namespace { 1567 1568 /** 1569 * Check if this insertion attempt would end up cutting one or more pivot 1570 * tables in half, which is not desirable. 1571 * 1572 * @return true if this insertion can be done safely without shearing any 1573 * existing pivot tables, false otherwise. 1574 */ 1575 bool canInsertCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, InsCellCmd eCmd, const ScDocument& rDoc) 1576 { 1577 if (!rDoc.HasPivotTable()) 1578 // This document has no pivot tables. 1579 return true; 1580 1581 const ScDPCollection* pDPs = rDoc.GetDPCollection(); 1582 1583 ScRange aRange(rRange); // local copy 1584 switch (eCmd) 1585 { 1586 case INS_INSROWS_BEFORE: 1587 { 1588 aRange.aStart.SetCol(0); 1589 aRange.aEnd.SetCol(rDoc.MaxCol()); 1590 [[fallthrough]]; 1591 } 1592 case INS_CELLSDOWN: 1593 { 1594 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) { 1595 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); }); 1596 if (bIntersects) 1597 // This column range cuts through at least one pivot table. Not good. 1598 return false; 1599 1600 // Start row must be either at the top or above any pivot tables. 1601 if (aRange.aStart.Row() < 0) 1602 // I don't know how to handle this case. 1603 return false; 1604 1605 if (aRange.aStart.Row() == 0) 1606 // First row is always allowed. 1607 return true; 1608 1609 ScRange aTest(aRange); 1610 aTest.aStart.IncRow(-1); // Test one row up. 1611 aTest.aEnd.SetRow(aTest.aStart.Row()); 1612 for (const auto& rTab : rMarkData) 1613 { 1614 aTest.aStart.SetTab(rTab); 1615 aTest.aEnd.SetTab(rTab); 1616 if (pDPs->HasTable(aTest)) 1617 return false; 1618 } 1619 } 1620 break; 1621 case INS_INSCOLS_BEFORE: 1622 { 1623 aRange.aStart.SetRow(0); 1624 aRange.aEnd.SetRow(rDoc.MaxRow()); 1625 [[fallthrough]]; 1626 } 1627 case INS_CELLSRIGHT: 1628 { 1629 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) { 1630 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); }); 1631 if (bIntersects) 1632 // This column range cuts through at least one pivot table. Not good. 1633 return false; 1634 1635 // Start row must be either at the top or above any pivot tables. 1636 if (aRange.aStart.Col() < 0) 1637 // I don't know how to handle this case. 1638 return false; 1639 1640 if (aRange.aStart.Col() == 0) 1641 // First row is always allowed. 1642 return true; 1643 1644 ScRange aTest(aRange); 1645 aTest.aStart.IncCol(-1); // Test one column to the left. 1646 aTest.aEnd.SetCol(aTest.aStart.Col()); 1647 for (const auto& rTab : rMarkData) 1648 { 1649 aTest.aStart.SetTab(rTab); 1650 aTest.aEnd.SetTab(rTab); 1651 if (pDPs->HasTable(aTest)) 1652 return false; 1653 } 1654 } 1655 break; 1656 default: 1657 ; 1658 } 1659 return true; 1660 } 1661 1662 /** 1663 * Check if this deletion attempt would end up cutting one or more pivot 1664 * tables in half, which is not desirable. 1665 * 1666 * @return true if this deletion can be done safely without shearing any 1667 * existing pivot tables, false otherwise. 1668 */ 1669 bool canDeleteCellsByPivot(const ScRange& rRange, const ScMarkData& rMarkData, DelCellCmd eCmd, const ScDocument& rDoc) 1670 { 1671 if (!rDoc.HasPivotTable()) 1672 // This document has no pivot tables. 1673 return true; 1674 1675 const ScDPCollection* pDPs = rDoc.GetDPCollection(); 1676 1677 ScRange aRange(rRange); // local copy 1678 1679 switch (eCmd) 1680 { 1681 case DelCellCmd::Rows: 1682 { 1683 aRange.aStart.SetCol(0); 1684 aRange.aEnd.SetCol(rDoc.MaxCol()); 1685 [[fallthrough]]; 1686 } 1687 case DelCellCmd::CellsUp: 1688 { 1689 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) { 1690 return pDPs->IntersectsTableByColumns(aRange.aStart.Col(), aRange.aEnd.Col(), aRange.aStart.Row(), rTab); }); 1691 if (bIntersects) 1692 // This column range cuts through at least one pivot table. Not good. 1693 return false; 1694 1695 ScRange aTest(aRange); 1696 for (const auto& rTab : rMarkData) 1697 { 1698 aTest.aStart.SetTab(rTab); 1699 aTest.aEnd.SetTab(rTab); 1700 if (pDPs->HasTable(aTest)) 1701 return false; 1702 } 1703 } 1704 break; 1705 case DelCellCmd::Cols: 1706 { 1707 aRange.aStart.SetRow(0); 1708 aRange.aEnd.SetRow(rDoc.MaxRow()); 1709 [[fallthrough]]; 1710 } 1711 case DelCellCmd::CellsLeft: 1712 { 1713 auto bIntersects = std::any_of(rMarkData.begin(), rMarkData.end(), [&pDPs, &aRange](const SCTAB& rTab) { 1714 return pDPs->IntersectsTableByRows(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Row(), rTab); }); 1715 if (bIntersects) 1716 // This column range cuts through at least one pivot table. Not good. 1717 return false; 1718 1719 ScRange aTest(aRange); 1720 for (const auto& rTab : rMarkData) 1721 { 1722 aTest.aStart.SetTab(rTab); 1723 aTest.aEnd.SetTab(rTab); 1724 if (pDPs->HasTable(aTest)) 1725 return false; 1726 } 1727 } 1728 break; 1729 default: 1730 ; 1731 } 1732 return true; 1733 } 1734 1735 } 1736 1737 bool ScDocFunc::InsertCells( const ScRange& rRange, const ScMarkData* pTabMark, InsCellCmd eCmd, 1738 bool bRecord, bool bApi, bool bPartOfPaste ) 1739 { 1740 ScDocShellModificator aModificator( rDocShell ); 1741 ScDocument& rDoc = rDocShell.GetDocument(); 1742 1743 if (rDocShell.GetDocument().GetChangeTrack() && 1744 ((eCmd == INS_CELLSDOWN && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) || 1745 (eCmd == INS_CELLSRIGHT && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow())))) 1746 { 1747 // We should not reach this via UI disabled slots. 1748 assert(bApi); 1749 SAL_WARN("sc.ui","ScDocFunc::InsertCells - no change-tracking of partial cell shift"); 1750 return false; 1751 } 1752 1753 ScRange aTargetRange( rRange ); 1754 1755 // If insertion is for full cols/rows and after the current 1756 // selection, then shift the range accordingly 1757 if ( eCmd == INS_INSROWS_AFTER ) 1758 { 1759 ScRange aErrorRange( ScAddress::UNINITIALIZED ); 1760 if (!aTargetRange.Move(0, rRange.aEnd.Row() - rRange.aStart.Row() + 1, 0, aErrorRange, rDoc)) 1761 { 1762 return false; 1763 } 1764 } 1765 if ( eCmd == INS_INSCOLS_AFTER ) 1766 { 1767 ScRange aErrorRange( ScAddress::UNINITIALIZED ); 1768 if (!aTargetRange.Move(rRange.aEnd.Col() - rRange.aStart.Col() + 1, 0, 0, aErrorRange, rDoc)) 1769 { 1770 return false; 1771 } 1772 } 1773 1774 SCCOL nStartCol = aTargetRange.aStart.Col(); 1775 SCROW nStartRow = aTargetRange.aStart.Row(); 1776 SCTAB nStartTab = aTargetRange.aStart.Tab(); 1777 SCCOL nEndCol = aTargetRange.aEnd.Col(); 1778 SCROW nEndRow = aTargetRange.aEnd.Row(); 1779 SCTAB nEndTab = aTargetRange.aEnd.Tab(); 1780 1781 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) ) 1782 { 1783 OSL_FAIL("invalid row in InsertCells"); 1784 return false; 1785 } 1786 1787 SCTAB nTabCount = rDoc.GetTableCount(); 1788 SCCOL nPaintStartCol = nStartCol; 1789 SCROW nPaintStartRow = nStartRow; 1790 SCCOL nPaintEndCol = nEndCol; 1791 SCROW nPaintEndRow = nEndRow; 1792 PaintPartFlags nPaintFlags = PaintPartFlags::Grid; 1793 bool bSuccess; 1794 1795 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); //preserve current cursor position 1796 SCCOL nCursorCol = 0; 1797 SCROW nCursorRow = 0; 1798 if( pViewSh ) 1799 { 1800 nCursorCol = pViewSh->GetViewData().GetCurX(); 1801 nCursorRow = pViewSh->GetViewData().GetCurY(); 1802 } 1803 1804 if (bRecord && !rDoc.IsUndoEnabled()) 1805 bRecord = false; 1806 1807 ScMarkData aMark(rDoc.GetSheetLimits()); 1808 if (pTabMark) 1809 aMark = *pTabMark; 1810 else 1811 { 1812 SCTAB nCount = 0; 1813 for( SCTAB i=0; i<nTabCount; i++ ) 1814 { 1815 if( !rDoc.IsScenario(i) ) 1816 { 1817 nCount++; 1818 if( nCount == nEndTab+1 ) 1819 { 1820 aMark.SelectTable( i, true ); 1821 break; 1822 } 1823 } 1824 } 1825 } 1826 1827 ScMarkData aFullMark( aMark ); // including scenario sheets 1828 for (const auto& rTab : aMark) 1829 { 1830 if (rTab >= nTabCount) 1831 break; 1832 1833 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 1834 aFullMark.SelectTable( j, true ); 1835 } 1836 1837 SCTAB nSelCount = aMark.GetSelectCount(); 1838 1839 // Adjust also related scenarios 1840 1841 SCCOL nMergeTestStartCol = nStartCol; 1842 SCROW nMergeTestStartRow = nStartRow; 1843 SCCOL nMergeTestEndCol = nEndCol; 1844 SCROW nMergeTestEndRow = nEndRow; 1845 1846 ScRange aExtendMergeRange( aTargetRange ); 1847 1848 if( aTargetRange.aStart == aTargetRange.aEnd && rDoc.HasAttrib(aTargetRange, HasAttrFlags::Merged) ) 1849 { 1850 rDoc.ExtendMerge( aExtendMergeRange ); 1851 rDoc.ExtendOverlapped( aExtendMergeRange ); 1852 nMergeTestEndCol = aExtendMergeRange.aEnd.Col(); 1853 nMergeTestEndRow = aExtendMergeRange.aEnd.Row(); 1854 nPaintEndCol = nMergeTestEndCol; 1855 nPaintEndRow = nMergeTestEndRow; 1856 } 1857 1858 if ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) 1859 { 1860 nMergeTestStartCol = 0; 1861 nMergeTestEndCol = rDoc.MaxCol(); 1862 } 1863 if ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER ) 1864 { 1865 nMergeTestStartRow = 0; 1866 nMergeTestEndRow = rDoc.MaxRow(); 1867 } 1868 if ( eCmd == INS_CELLSDOWN ) 1869 nMergeTestEndRow = rDoc.MaxRow(); 1870 if ( eCmd == INS_CELLSRIGHT ) 1871 nMergeTestEndCol = rDoc.MaxCol(); 1872 1873 bool bNeedRefresh = false; 1874 1875 SCCOL nEditTestEndCol = (eCmd==INS_INSCOLS_BEFORE || eCmd==INS_INSCOLS_AFTER) ? rDoc.MaxCol() : nMergeTestEndCol; 1876 SCROW nEditTestEndRow = (eCmd==INS_INSROWS_BEFORE || eCmd==INS_INSROWS_AFTER) ? rDoc.MaxRow() : nMergeTestEndRow; 1877 1878 ScEditableTester aTester; 1879 1880 switch (eCmd) 1881 { 1882 case INS_INSCOLS_BEFORE: 1883 aTester = ScEditableTester( 1884 rDoc, sc::ColRowEditAction::InsertColumnsBefore, nMergeTestStartCol, nMergeTestEndCol, aMark); 1885 break; 1886 case INS_INSCOLS_AFTER: 1887 aTester = ScEditableTester( 1888 rDoc, sc::ColRowEditAction::InsertColumnsAfter, nMergeTestStartCol, nMergeTestEndCol, aMark); 1889 break; 1890 case INS_INSROWS_BEFORE: 1891 aTester = ScEditableTester( 1892 rDoc, sc::ColRowEditAction::InsertRowsBefore, nMergeTestStartRow, nMergeTestEndRow, aMark); 1893 break; 1894 case INS_INSROWS_AFTER: 1895 aTester = ScEditableTester( 1896 rDoc, sc::ColRowEditAction::InsertRowsAfter, nMergeTestStartRow, nMergeTestEndRow, aMark); 1897 break; 1898 default: 1899 aTester = ScEditableTester( 1900 rDoc, nMergeTestStartCol, nMergeTestStartRow, nEditTestEndCol, nEditTestEndRow, aMark); 1901 } 1902 1903 if (!aTester.IsEditable()) 1904 { 1905 if (!bApi) 1906 rDocShell.ErrorMessage(aTester.GetMessageId()); 1907 return false; 1908 } 1909 1910 // Check if this insertion is allowed with respect to pivot table. 1911 if (!canInsertCellsByPivot(aTargetRange, aMark, eCmd, rDoc)) 1912 { 1913 if (!bApi) 1914 rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE); 1915 return false; 1916 } 1917 1918 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important due to TrackFormulas at UpdateReference 1919 1920 ScDocumentUniquePtr pRefUndoDoc; 1921 std::unique_ptr<ScRefUndoData> pUndoData; 1922 if ( bRecord ) 1923 { 1924 pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1925 pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 ); 1926 1927 // pRefUndoDoc is filled in InsertCol / InsertRow 1928 1929 pUndoData.reset(new ScRefUndoData( &rDoc )); 1930 1931 rDoc.BeginDrawUndo(); 1932 } 1933 1934 // #i8302 : we unmerge overwhelming ranges, before insertion all the actions are put in the same ListAction 1935 // the patch comes from mloiseleur and maoyg 1936 bool bInsertMerge = false; 1937 std::vector<ScRange> qIncreaseRange; 1938 OUString aUndo = ScResId( STR_UNDO_INSERTCELLS ); 1939 if (bRecord) 1940 { 1941 ViewShellId nViewShellId(-1); 1942 if (pViewSh) 1943 nViewShellId = pViewSh->GetViewShellId(); 1944 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId ); 1945 } 1946 std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge; 1947 1948 for (const SCTAB i : aMark) 1949 { 1950 if (i >= nTabCount) 1951 break; 1952 1953 if( rDoc.HasAttrib( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) 1954 { 1955 if (eCmd==INS_CELLSRIGHT) 1956 bNeedRefresh = true; 1957 1958 SCCOL nMergeStartCol = nMergeTestStartCol; 1959 SCROW nMergeStartRow = nMergeTestStartRow; 1960 SCCOL nMergeEndCol = nMergeTestEndCol; 1961 SCROW nMergeEndRow = nMergeTestEndRow; 1962 1963 rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i ); 1964 rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i ); 1965 1966 if(( eCmd == INS_CELLSDOWN && ( nMergeStartCol != nMergeTestStartCol || nMergeEndCol != nMergeTestEndCol )) || 1967 (eCmd == INS_CELLSRIGHT && ( nMergeStartRow != nMergeTestStartRow || nMergeEndRow != nMergeTestEndRow )) ) 1968 { 1969 if (!bApi) 1970 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); 1971 rDocShell.GetUndoManager()->LeaveListAction(); 1972 return false; 1973 } 1974 1975 SCCOL nTestCol = -1; 1976 SCROW nTestRow1 = -1; 1977 SCROW nTestRow2 = -1; 1978 1979 ScDocAttrIterator aTestIter( rDoc, i, nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow ); 1980 ScRange aExtendRange( nMergeTestStartCol, nMergeTestStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i ); 1981 const ScPatternAttr* pPattern = nullptr; 1982 while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr ) 1983 { 1984 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE); 1985 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG); 1986 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver); 1987 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver) 1988 { 1989 ScRange aRange( nTestCol, nTestRow1, i ); 1990 rDoc.ExtendOverlapped(aRange); 1991 rDoc.ExtendMerge(aRange, true); 1992 1993 if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor ) 1994 { 1995 for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ ) 1996 { 1997 ScRange aTestRange( nTestCol, nTestRow, i ); 1998 rDoc.ExtendOverlapped( aTestRange ); 1999 rDoc.ExtendMerge( aTestRange, true); 2000 ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i ); 2001 if( !aExtendRange.Contains( aMergeRange ) ) 2002 { 2003 qIncreaseRange.push_back( aTestRange ); 2004 bInsertMerge = true; 2005 } 2006 } 2007 } 2008 else 2009 { 2010 ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i ); 2011 if( !aExtendRange.Contains( aMergeRange ) ) 2012 { 2013 qIncreaseRange.push_back( aRange ); 2014 } 2015 bInsertMerge = true; 2016 } 2017 } 2018 } 2019 2020 if( bInsertMerge ) 2021 { 2022 if( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER || eCmd == INS_CELLSDOWN ) 2023 { 2024 nStartRow = aExtendMergeRange.aStart.Row(); 2025 nEndRow = aExtendMergeRange.aEnd.Row(); 2026 2027 if( eCmd == INS_CELLSDOWN ) 2028 nEndCol = nMergeTestEndCol; 2029 else 2030 { 2031 nStartCol = 0; 2032 nEndCol = rDoc.MaxCol(); 2033 } 2034 } 2035 else if( eCmd == INS_CELLSRIGHT || eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER ) 2036 { 2037 2038 nStartCol = aExtendMergeRange.aStart.Col(); 2039 nEndCol = aExtendMergeRange.aEnd.Col(); 2040 if( eCmd == INS_CELLSRIGHT ) 2041 { 2042 nEndRow = nMergeTestEndRow; 2043 } 2044 else 2045 { 2046 nStartRow = 0; 2047 nEndRow = rDoc.MaxRow(); 2048 } 2049 } 2050 2051 if( !qIncreaseRange.empty() ) 2052 { 2053 if (bRecord && !pUndoRemoveMerge) 2054 { 2055 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 2056 pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin()); 2057 pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) )); 2058 } 2059 2060 for( const ScRange& aRange : qIncreaseRange ) 2061 { 2062 if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) ) 2063 { 2064 UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() ); 2065 } 2066 } 2067 } 2068 } 2069 else 2070 { 2071 if (!bApi) 2072 rDocShell.ErrorMessage(STR_MSSG_INSERTCELLS_0); 2073 rDocShell.GetUndoManager()->LeaveListAction(); 2074 return false; 2075 } 2076 } 2077 } 2078 2079 if (bRecord && pUndoRemoveMerge) 2080 { 2081 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge)); 2082 } 2083 2084 switch (eCmd) 2085 { 2086 case INS_CELLSDOWN: 2087 bSuccess = rDoc.InsertRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark ); 2088 nPaintEndRow = rDoc.MaxRow(); 2089 break; 2090 case INS_INSROWS_BEFORE: 2091 case INS_INSROWS_AFTER: 2092 bSuccess = rDoc.InsertRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &aFullMark ); 2093 nPaintStartCol = 0; 2094 nPaintEndCol = rDoc.MaxCol(); 2095 nPaintEndRow = rDoc.MaxRow(); 2096 nPaintFlags |= PaintPartFlags::Left; 2097 break; 2098 case INS_CELLSRIGHT: 2099 bSuccess = rDoc.InsertCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark ); 2100 nPaintEndCol = rDoc.MaxCol(); 2101 break; 2102 case INS_INSCOLS_BEFORE: 2103 case INS_INSCOLS_AFTER: 2104 bSuccess = rDoc.InsertCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &aFullMark ); 2105 nPaintStartRow = 0; 2106 nPaintEndRow = rDoc.MaxRow(); 2107 nPaintEndCol = rDoc.MaxCol(); 2108 nPaintFlags |= PaintPartFlags::Top; 2109 break; 2110 default: 2111 OSL_FAIL("Wrong code at inserting"); 2112 bSuccess = false; 2113 break; 2114 } 2115 2116 if ( bSuccess ) 2117 { 2118 SCTAB nUndoPos = 0; 2119 2120 if ( bRecord ) 2121 { 2122 std::unique_ptr<SCTAB[]> pTabs(new SCTAB[nSelCount]); 2123 std::unique_ptr<SCTAB[]> pScenarios(new SCTAB[nSelCount]); 2124 nUndoPos = 0; 2125 for (const auto& rTab : aMark) 2126 { 2127 if (rTab >= nTabCount) 2128 break; 2129 2130 SCTAB nCount = 0; 2131 for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2132 nCount ++; 2133 2134 pScenarios[nUndoPos] = nCount; 2135 pTabs[nUndoPos] = rTab; 2136 nUndoPos ++; 2137 } 2138 2139 if( !bInsertMerge ) 2140 { 2141 rDocShell.GetUndoManager()->LeaveListAction(); 2142 } 2143 2144 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertCells>( 2145 &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ), 2146 nUndoPos, std::move(pTabs), std::move(pScenarios), eCmd, std::move(pRefUndoDoc), std::move(pUndoData), bPartOfPaste ) ); 2147 } 2148 2149 // #i8302 : we remerge growing ranges, with the new part inserted 2150 2151 while( !qIncreaseRange.empty() ) 2152 { 2153 ScRange aRange = qIncreaseRange.back(); 2154 if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) ) 2155 { 2156 switch (eCmd) 2157 { 2158 case INS_CELLSDOWN: 2159 case INS_INSROWS_BEFORE: 2160 case INS_INSROWS_AFTER: 2161 aRange.aEnd.IncRow(static_cast<SCCOL>(nEndRow-nStartRow+1)); 2162 break; 2163 case INS_CELLSRIGHT: 2164 case INS_INSCOLS_BEFORE: 2165 case INS_INSCOLS_AFTER: 2166 aRange.aEnd.IncCol(static_cast<SCCOL>(nEndCol-nStartCol+1)); 2167 break; 2168 default: 2169 break; 2170 } 2171 ScCellMergeOption aMergeOption( 2172 aRange.aStart.Col(), aRange.aStart.Row(), 2173 aRange.aEnd.Col(), aRange.aEnd.Row() ); 2174 aMergeOption.maTabs.insert(aRange.aStart.Tab()); 2175 MergeCells(aMergeOption, false, true, true); 2176 } 2177 qIncreaseRange.pop_back(); 2178 } 2179 2180 if( bInsertMerge ) 2181 rDocShell.GetUndoManager()->LeaveListAction(); 2182 2183 for (const SCTAB i : aMark) 2184 { 2185 if (i >= nTabCount) 2186 break; 2187 2188 rDoc.SetDrawPageSize(i); 2189 2190 if (bNeedRefresh) 2191 rDoc.ExtendMerge( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i, true ); 2192 else 2193 rDoc.RefreshAutoFilter( nMergeTestStartCol, nMergeTestStartRow, nMergeTestEndCol, nMergeTestEndRow, i ); 2194 2195 if ( eCmd == INS_INSROWS_BEFORE ||eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSROWS_AFTER ||eCmd == INS_INSCOLS_AFTER ) 2196 rDoc.UpdatePageBreaks( i ); 2197 2198 sal_uInt16 nExtFlags = 0; 2199 rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i ); 2200 2201 SCTAB nScenarioCount = 0; 2202 2203 for( SCTAB j = i+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2204 nScenarioCount ++; 2205 2206 bool bAdjusted = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ) ? 2207 AdjustRowHeight(ScRange(0, nStartRow, i, rDoc.MaxCol(), nEndRow, i+nScenarioCount ), true, bApi) : 2208 AdjustRowHeight(ScRange(0, nPaintStartRow, i, rDoc.MaxCol(), nPaintEndRow, i+nScenarioCount ), true, bApi); 2209 if (bAdjusted) 2210 { 2211 // paint only what is not done by AdjustRowHeight 2212 if (nPaintFlags & PaintPartFlags::Top) 2213 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, PaintPartFlags::Top ); 2214 } 2215 else 2216 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, i, nPaintEndCol, nPaintEndRow, i+nScenarioCount, nPaintFlags, nExtFlags ); 2217 } 2218 } 2219 else 2220 { 2221 if( bInsertMerge ) 2222 { 2223 while( !qIncreaseRange.empty() ) 2224 { 2225 ScRange aRange = qIncreaseRange.back(); 2226 ScCellMergeOption aMergeOption( 2227 aRange.aStart.Col(), aRange.aStart.Row(), 2228 aRange.aEnd.Col(), aRange.aEnd.Row() ); 2229 MergeCells(aMergeOption, false, true, true); 2230 qIncreaseRange.pop_back(); 2231 } 2232 2233 if( pViewSh ) 2234 { 2235 pViewSh->MarkRange( aTargetRange, false ); 2236 pViewSh->SetCursor( nCursorCol, nCursorRow ); 2237 } 2238 } 2239 2240 rDocShell.GetUndoManager()->LeaveListAction(); 2241 rDocShell.GetUndoManager()->RemoveLastUndoAction(); 2242 2243 pRefUndoDoc.reset(); 2244 if (!bApi) 2245 rDocShell.ErrorMessage(STR_INSERT_FULL); // column/row full 2246 } 2247 2248 // The cursor position needs to be modified earlier than updating 2249 // any enabled edit view which is triggered by SetDocumentModified below. 2250 if (bSuccess) 2251 { 2252 bool bInsertCols = ( eCmd == INS_INSCOLS_BEFORE || eCmd == INS_INSCOLS_AFTER); 2253 bool bInsertRows = ( eCmd == INS_INSROWS_BEFORE || eCmd == INS_INSROWS_AFTER ); 2254 2255 if (bInsertCols) 2256 { 2257 pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), 1); 2258 } 2259 2260 if (bInsertRows) 2261 { 2262 pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), 1); 2263 } 2264 } 2265 2266 aModificator.SetDocumentModified(); 2267 2268 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); 2269 return bSuccess; 2270 } 2271 2272 bool ScDocFunc::DeleteCells( const ScRange& rRange, const ScMarkData* pTabMark, DelCellCmd eCmd, 2273 bool bApi ) 2274 { 2275 ScDocShellModificator aModificator( rDocShell ); 2276 ScDocument& rDoc = rDocShell.GetDocument(); 2277 2278 if (rDocShell.GetDocument().GetChangeTrack() && 2279 ((eCmd == DelCellCmd::CellsUp && (rRange.aStart.Col() != 0 || rRange.aEnd.Col() != rDoc.MaxCol())) || 2280 (eCmd == DelCellCmd::CellsLeft && (rRange.aStart.Row() != 0 || rRange.aEnd.Row() != rDoc.MaxRow())))) 2281 { 2282 // We should not reach this via UI disabled slots. 2283 assert(bApi); 2284 SAL_WARN("sc.ui","ScDocFunc::DeleteCells - no change-tracking of partial cell shift"); 2285 return false; 2286 } 2287 2288 SCCOL nStartCol = rRange.aStart.Col(); 2289 SCROW nStartRow = rRange.aStart.Row(); 2290 SCTAB nStartTab = rRange.aStart.Tab(); 2291 SCCOL nEndCol = rRange.aEnd.Col(); 2292 SCROW nEndRow = rRange.aEnd.Row(); 2293 SCTAB nEndTab = rRange.aEnd.Tab(); 2294 2295 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) ) 2296 { 2297 OSL_FAIL("invalid row in DeleteCells"); 2298 return false; 2299 } 2300 2301 SCTAB nTabCount = rDoc.GetTableCount(); 2302 SCCOL nPaintStartCol = nStartCol; 2303 SCROW nPaintStartRow = nStartRow; 2304 SCCOL nPaintEndCol = nEndCol; 2305 SCROW nPaintEndRow = nEndRow; 2306 PaintPartFlags nPaintFlags = PaintPartFlags::Grid; 2307 2308 bool bRecord = true; 2309 if (!rDoc.IsUndoEnabled()) 2310 bRecord = false; 2311 2312 ScMarkData aMark(rDoc.GetSheetLimits()); 2313 if (pTabMark) 2314 aMark = *pTabMark; 2315 else 2316 { 2317 SCTAB nCount = 0; 2318 for(SCTAB i=0; i<nTabCount; i++ ) 2319 { 2320 if( !rDoc.IsScenario(i) ) 2321 { 2322 nCount++; 2323 if( nCount == nEndTab+1 ) 2324 { 2325 aMark.SelectTable(i, true); 2326 break; 2327 } 2328 } 2329 } 2330 } 2331 2332 ScMarkData aFullMark( aMark ); // including scenario sheets 2333 for (const auto& rTab : aMark) 2334 { 2335 if (rTab >= nTabCount) 2336 break; 2337 2338 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2339 aFullMark.SelectTable( j, true ); 2340 } 2341 2342 SCTAB nSelCount = aMark.GetSelectCount(); 2343 2344 SCCOL nUndoStartCol = nStartCol; 2345 SCROW nUndoStartRow = nStartRow; 2346 SCCOL nUndoEndCol = nEndCol; 2347 SCROW nUndoEndRow = nEndRow; 2348 2349 ScRange aExtendMergeRange( rRange ); 2350 2351 if( rRange.aStart == rRange.aEnd && rDoc.HasAttrib(rRange, HasAttrFlags::Merged) ) 2352 { 2353 rDoc.ExtendMerge( aExtendMergeRange ); 2354 rDoc.ExtendOverlapped( aExtendMergeRange ); 2355 nUndoEndCol = aExtendMergeRange.aEnd.Col(); 2356 nUndoEndRow = aExtendMergeRange.aEnd.Row(); 2357 nPaintEndCol = nUndoEndCol; 2358 nPaintEndRow = nUndoEndRow; 2359 } 2360 2361 if (eCmd==DelCellCmd::Rows) 2362 { 2363 nUndoStartCol = 0; 2364 nUndoEndCol = rDoc.MaxCol(); 2365 } 2366 if (eCmd==DelCellCmd::Cols) 2367 { 2368 nUndoStartRow = 0; 2369 nUndoEndRow = rDoc.MaxRow(); 2370 } 2371 // Test for cell protection 2372 2373 SCCOL nEditTestEndX = nUndoEndCol; 2374 if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft ) 2375 nEditTestEndX = rDoc.MaxCol(); 2376 SCROW nEditTestEndY = nUndoEndRow; 2377 if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp ) 2378 nEditTestEndY = rDoc.MaxRow(); 2379 2380 ScEditableTester aTester; 2381 2382 switch (eCmd) 2383 { 2384 case DelCellCmd::Cols: 2385 aTester = ScEditableTester( 2386 rDoc, sc::ColRowEditAction::DeleteColumns, nUndoStartCol, nUndoEndCol, aMark); 2387 break; 2388 case DelCellCmd::Rows: 2389 aTester = ScEditableTester( 2390 rDoc, sc::ColRowEditAction::DeleteRows, nUndoStartRow, nUndoEndRow, aMark); 2391 break; 2392 default: 2393 aTester = ScEditableTester( 2394 rDoc, nUndoStartCol, nUndoStartRow, nEditTestEndX, nEditTestEndY, aMark); 2395 } 2396 2397 if (!aTester.IsEditable()) 2398 { 2399 if (!bApi) 2400 rDocShell.ErrorMessage(aTester.GetMessageId()); 2401 return false; 2402 } 2403 2404 if (!canDeleteCellsByPivot(rRange, aMark, eCmd, rDoc)) 2405 { 2406 if (!bApi) 2407 rDocShell.ErrorMessage(STR_NO_INSERT_DELETE_OVER_PIVOT_TABLE); 2408 return false; 2409 } 2410 // Test for merged cells 2411 2412 SCCOL nMergeTestEndCol = (eCmd==DelCellCmd::CellsLeft) ? rDoc.MaxCol() : nUndoEndCol; 2413 SCROW nMergeTestEndRow = (eCmd==DelCellCmd::CellsUp) ? rDoc.MaxRow() : nUndoEndRow; 2414 SCCOL nExtendStartCol = nUndoStartCol; 2415 SCROW nExtendStartRow = nUndoStartRow; 2416 bool bNeedRefresh = false; 2417 2418 //Issue 8302 want to be able to insert into the middle of merged cells 2419 //the patch comes from maoyg 2420 ::std::vector<ScRange> qDecreaseRange; 2421 bool bDeletingMerge = false; 2422 OUString aUndo = ScResId( STR_UNDO_DELETECELLS ); 2423 if (bRecord) 2424 { 2425 ViewShellId nViewShellId(-1); 2426 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) 2427 nViewShellId = pViewSh->GetViewShellId(); 2428 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId ); 2429 } 2430 std::unique_ptr<ScUndoRemoveMerge> pUndoRemoveMerge; 2431 2432 for (const SCTAB i : aMark) 2433 { 2434 if (i >= nTabCount) 2435 break; 2436 2437 if ( rDoc.HasAttrib( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i, HasAttrFlags::Merged | HasAttrFlags::Overlapped )) 2438 { 2439 SCCOL nMergeStartCol = nUndoStartCol; 2440 SCROW nMergeStartRow = nUndoStartRow; 2441 SCCOL nMergeEndCol = nMergeTestEndCol; 2442 SCROW nMergeEndRow = nMergeTestEndRow; 2443 2444 rDoc.ExtendMerge( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i ); 2445 rDoc.ExtendOverlapped( nMergeStartCol, nMergeStartRow, nMergeEndCol, nMergeEndRow, i ); 2446 if( ( eCmd == DelCellCmd::CellsUp && ( nMergeStartCol != nUndoStartCol || nMergeEndCol != nMergeTestEndCol))|| 2447 ( eCmd == DelCellCmd::CellsLeft && ( nMergeStartRow != nUndoStartRow || nMergeEndRow != nMergeTestEndRow))) 2448 { 2449 if (!bApi) 2450 rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0); 2451 rDocShell.GetUndoManager()->LeaveListAction(); 2452 return false; 2453 } 2454 2455 nExtendStartCol = nMergeStartCol; 2456 nExtendStartRow = nMergeStartRow; 2457 SCCOL nTestCol = -1; 2458 SCROW nTestRow1 = -1; 2459 SCROW nTestRow2 = -1; 2460 2461 ScDocAttrIterator aTestIter( rDoc, i, nUndoStartCol, nUndoStartRow, nMergeTestEndCol, nMergeTestEndRow ); 2462 ScRange aExtendRange( nUndoStartCol, nUndoStartRow, i, nMergeTestEndCol, nMergeTestEndRow, i ); 2463 const ScPatternAttr* pPattern = nullptr; 2464 while ( ( pPattern = aTestIter.GetNext( nTestCol, nTestRow1, nTestRow2 ) ) != nullptr ) 2465 { 2466 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE); 2467 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG); 2468 ScMF nNewFlags = rMergeFlagAttr.GetValue() & (ScMF::Hor | ScMF::Ver); 2469 if (rMergeFlag.IsMerged() || nNewFlags == ScMF::Hor || nNewFlags == ScMF::Ver) 2470 { 2471 ScRange aRange( nTestCol, nTestRow1, i ); 2472 rDoc.ExtendOverlapped( aRange ); 2473 rDoc.ExtendMerge( aRange, true ); 2474 2475 if( nTestRow1 < nTestRow2 && nNewFlags == ScMF::Hor ) 2476 { 2477 for( SCROW nTestRow = nTestRow1; nTestRow <= nTestRow2; nTestRow++ ) 2478 { 2479 ScRange aTestRange( nTestCol, nTestRow, i ); 2480 rDoc.ExtendOverlapped( aTestRange ); 2481 rDoc.ExtendMerge( aTestRange, true ); 2482 ScRange aMergeRange( aTestRange.aStart.Col(),aTestRange.aStart.Row(), i ); 2483 if( !aExtendRange.Contains( aMergeRange ) ) 2484 { 2485 qDecreaseRange.push_back( aTestRange ); 2486 bDeletingMerge = true; 2487 } 2488 } 2489 } 2490 else 2491 { 2492 ScRange aMergeRange( aRange.aStart.Col(),aRange.aStart.Row(), i ); 2493 if( !aExtendRange.Contains( aMergeRange ) ) 2494 { 2495 qDecreaseRange.push_back( aRange ); 2496 } 2497 bDeletingMerge = true; 2498 } 2499 } 2500 } 2501 2502 if( bDeletingMerge ) 2503 { 2504 2505 if( eCmd == DelCellCmd::Rows || eCmd == DelCellCmd::CellsUp ) 2506 { 2507 nStartRow = aExtendMergeRange.aStart.Row(); 2508 nEndRow = aExtendMergeRange.aEnd.Row(); 2509 bNeedRefresh = true; 2510 2511 if( eCmd == DelCellCmd::CellsUp ) 2512 { 2513 nEndCol = aExtendMergeRange.aEnd.Col(); 2514 } 2515 else 2516 { 2517 nStartCol = 0; 2518 nEndCol = rDoc.MaxCol(); 2519 } 2520 } 2521 else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols ) 2522 { 2523 2524 nStartCol = aExtendMergeRange.aStart.Col(); 2525 nEndCol = aExtendMergeRange.aEnd.Col(); 2526 if( eCmd == DelCellCmd::CellsLeft ) 2527 { 2528 nEndRow = aExtendMergeRange.aEnd.Row(); 2529 bNeedRefresh = true; 2530 } 2531 else 2532 { 2533 nStartRow = 0; 2534 nEndRow = rDoc.MaxRow(); 2535 } 2536 } 2537 2538 if( !qDecreaseRange.empty() ) 2539 { 2540 if (bRecord && !pUndoRemoveMerge) 2541 { 2542 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 2543 pUndoDoc->InitUndo( rDoc, *aMark.begin(), *aMark.rbegin()); 2544 pUndoRemoveMerge.reset( new ScUndoRemoveMerge( &rDocShell, rRange, std::move(pUndoDoc) )); 2545 } 2546 2547 for( const ScRange& aRange : qDecreaseRange ) 2548 { 2549 if( rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) ) 2550 { 2551 UnmergeCells( aRange, bRecord, pUndoRemoveMerge.get() ); 2552 } 2553 } 2554 } 2555 } 2556 else 2557 { 2558 if (!bApi) 2559 rDocShell.ErrorMessage(STR_MSSG_DELETECELLS_0); 2560 rDocShell.GetUndoManager()->LeaveListAction(); 2561 return false; 2562 } 2563 } 2564 } 2565 2566 if (bRecord && pUndoRemoveMerge) 2567 { 2568 rDocShell.GetUndoManager()->AddUndoAction( std::move(pUndoRemoveMerge)); 2569 } 2570 2571 // do it 2572 2573 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); // important because of TrackFormulas in UpdateReference 2574 2575 ScDocumentUniquePtr pUndoDoc; 2576 std::unique_ptr<ScDocument> pRefUndoDoc; 2577 std::unique_ptr<ScRefUndoData> pUndoData; 2578 if ( bRecord ) 2579 { 2580 // With the fix for #101329#, UpdateRef always puts cells into pRefUndoDoc at their old position, 2581 // so it's no longer necessary to copy more than the deleted range into pUndoDoc. 2582 2583 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 2584 pUndoDoc->InitUndo( rDoc, 0, nTabCount-1, (eCmd==DelCellCmd::Cols), (eCmd==DelCellCmd::Rows) ); 2585 for (const auto& rTab : aMark) 2586 { 2587 if (rTab >= nTabCount) 2588 break; 2589 2590 SCTAB nScenarioCount = 0; 2591 2592 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2593 nScenarioCount ++; 2594 2595 rDoc.CopyToDocument( nUndoStartCol, nUndoStartRow, rTab, nUndoEndCol, nUndoEndRow, rTab+nScenarioCount, 2596 InsertDeleteFlags::ALL | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc ); 2597 } 2598 2599 pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 2600 pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 ); 2601 2602 pUndoData.reset(new ScRefUndoData( &rDoc )); 2603 2604 rDoc.BeginDrawUndo(); 2605 } 2606 2607 sal_uInt16 nExtFlags = 0; 2608 for (const auto& rTab : aMark) 2609 { 2610 if (rTab >= nTabCount) 2611 break; 2612 2613 rDocShell.UpdatePaintExt( nExtFlags, nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab ); 2614 } 2615 2616 switch (eCmd) 2617 { 2618 case DelCellCmd::CellsUp: 2619 case DelCellCmd::CellsLeft: 2620 rDoc.DeleteObjectsInArea(nStartCol, nStartRow, nEndCol, nEndRow, aMark, true); 2621 break; 2622 case DelCellCmd::Rows: 2623 rDoc.DeleteObjectsInArea(0, nStartRow, rDoc.MaxCol(), nEndRow, aMark, true); 2624 break; 2625 case DelCellCmd::Cols: 2626 rDoc.DeleteObjectsInArea(nStartCol, 0, nEndCol, rDoc.MaxRow(), aMark, true); 2627 break; 2628 default: 2629 break; 2630 } 2631 2632 2633 bool bUndoOutline = false; 2634 switch (eCmd) 2635 { 2636 case DelCellCmd::CellsUp: 2637 rDoc.DeleteRow( nStartCol, 0, nEndCol, MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), nullptr, &aFullMark ); 2638 nPaintEndRow = rDoc.MaxRow(); 2639 break; 2640 case DelCellCmd::Rows: 2641 rDoc.DeleteRow( 0, 0, rDoc.MaxCol(), MAXTAB, nStartRow, static_cast<SCSIZE>(nEndRow-nStartRow+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark ); 2642 nPaintStartCol = 0; 2643 nPaintEndCol = rDoc.MaxCol(); 2644 nPaintEndRow = rDoc.MaxRow(); 2645 nPaintFlags |= PaintPartFlags::Left; 2646 break; 2647 case DelCellCmd::CellsLeft: 2648 rDoc.DeleteCol( nStartRow, 0, nEndRow, MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), nullptr, &aFullMark ); 2649 nPaintEndCol = rDoc.MaxCol(); 2650 break; 2651 case DelCellCmd::Cols: 2652 rDoc.DeleteCol( 0, 0, rDoc.MaxRow(), MAXTAB, nStartCol, static_cast<SCSIZE>(nEndCol-nStartCol+1), pRefUndoDoc.get(), &bUndoOutline, &aFullMark ); 2653 nPaintStartRow = 0; 2654 nPaintEndRow = rDoc.MaxRow(); 2655 nPaintEndCol = rDoc.MaxCol(); 2656 nPaintFlags |= PaintPartFlags::Top; 2657 break; 2658 default: 2659 OSL_FAIL("Wrong code at deleting"); 2660 break; 2661 } 2662 2663 //! Test if the size of outline has changed 2664 2665 if ( bRecord ) 2666 { 2667 for (const auto& rTab : aFullMark) 2668 { 2669 if (rTab >= nTabCount) 2670 break; 2671 2672 pRefUndoDoc->DeleteAreaTab(nUndoStartCol,nUndoStartRow,nUndoEndCol,nUndoEndRow, rTab, InsertDeleteFlags::ALL); 2673 } 2674 2675 // for all sheets, so that formulas can be copied 2676 pUndoDoc->AddUndoTab( 0, nTabCount-1 ); 2677 2678 // copy with bColRowFlags=false (#54194#) 2679 pRefUndoDoc->CopyToDocument(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB,InsertDeleteFlags::FORMULA,false,*pUndoDoc,nullptr,false); 2680 pRefUndoDoc.reset(); 2681 2682 std::unique_ptr<SCTAB[]> pTabs( new SCTAB[nSelCount]); 2683 std::unique_ptr<SCTAB[]> pScenarios( new SCTAB[nSelCount]); 2684 SCTAB nUndoPos = 0; 2685 2686 for (const auto& rTab : aMark) 2687 { 2688 if (rTab >= nTabCount) 2689 break; 2690 2691 SCTAB nCount = 0; 2692 for( SCTAB j=rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2693 nCount ++; 2694 2695 pScenarios[nUndoPos] = nCount; 2696 pTabs[nUndoPos] = rTab; 2697 nUndoPos ++; 2698 } 2699 2700 if( !bDeletingMerge ) 2701 { 2702 rDocShell.GetUndoManager()->LeaveListAction(); 2703 } 2704 2705 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDeleteCells>( 2706 &rDocShell, ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ), 2707 nUndoPos, std::move(pTabs), std::move(pScenarios), 2708 eCmd, std::move(pUndoDoc), std::move(pUndoData) ) ); 2709 } 2710 2711 // #i8302 want to be able to insert into the middle of merged cells 2712 // the patch comes from maoyg 2713 2714 while( !qDecreaseRange.empty() ) 2715 { 2716 ScRange aRange = qDecreaseRange.back(); 2717 2718 sal_Int32 nDecreaseRowCount = 0; 2719 sal_Int32 nDecreaseColCount = 0; 2720 if( eCmd == DelCellCmd::CellsUp || eCmd == DelCellCmd::Rows ) 2721 { 2722 if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() ) 2723 nDecreaseRowCount = nEndRow-nStartRow+1; 2724 else if( nStartRow >= aRange.aStart.Row() && nStartRow <= aRange.aEnd.Row() && nEndRow >= aRange.aStart.Row() && nEndRow >= aRange.aEnd.Row() ) 2725 nDecreaseRowCount = aRange.aEnd.Row()-nStartRow+1; 2726 else if( nStartRow >= aRange.aStart.Row() && nStartRow >= aRange.aEnd.Row() && nEndRow>= aRange.aStart.Row() && nEndRow <= aRange.aEnd.Row() ) 2727 nDecreaseRowCount = aRange.aEnd.Row()-nEndRow+1; 2728 } 2729 else if( eCmd == DelCellCmd::CellsLeft || eCmd == DelCellCmd::Cols ) 2730 { 2731 if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() ) 2732 nDecreaseColCount = nEndCol-nStartCol+1; 2733 else if( nStartCol >= aRange.aStart.Col() && nStartCol <= aRange.aEnd.Col() && nEndCol >= aRange.aStart.Col() && nEndCol >= aRange.aEnd.Col() ) 2734 nDecreaseColCount = aRange.aEnd.Col()-nStartCol+1; 2735 else if( nStartCol >= aRange.aStart.Col() && nStartCol >= aRange.aEnd.Col() && nEndCol>= aRange.aStart.Col() && nEndCol <= aRange.aEnd.Col() ) 2736 nDecreaseColCount = aRange.aEnd.Col()-nEndCol+1; 2737 } 2738 2739 switch (eCmd) 2740 { 2741 case DelCellCmd::CellsUp: 2742 case DelCellCmd::Rows: 2743 aRange.aEnd.SetRow(static_cast<SCCOL>( aRange.aEnd.Row()-nDecreaseRowCount)); 2744 break; 2745 case DelCellCmd::CellsLeft: 2746 case DelCellCmd::Cols: 2747 aRange.aEnd.SetCol(static_cast<SCCOL>( aRange.aEnd.Col()-nDecreaseColCount)); 2748 break; 2749 default: 2750 break; 2751 } 2752 2753 if( !rDoc.HasAttrib( aRange, HasAttrFlags::Overlapped | HasAttrFlags::Merged ) ) 2754 { 2755 ScCellMergeOption aMergeOption(aRange); 2756 MergeCells( aMergeOption, false, true, true ); 2757 } 2758 qDecreaseRange.pop_back(); 2759 } 2760 2761 if( bDeletingMerge ) 2762 rDocShell.GetUndoManager()->LeaveListAction(); 2763 2764 if ( eCmd==DelCellCmd::Cols || eCmd==DelCellCmd::CellsLeft ) 2765 nMergeTestEndCol = rDoc.MaxCol(); 2766 if ( eCmd==DelCellCmd::Rows || eCmd==DelCellCmd::CellsUp ) 2767 nMergeTestEndRow = rDoc.MaxRow(); 2768 if ( bNeedRefresh ) 2769 { 2770 // #i51445# old merge flag attributes must be deleted also for single cells, 2771 // not only for whole columns/rows 2772 2773 ScPatternAttr aPattern( rDoc.GetPool() ); 2774 aPattern.GetItemSet().Put( ScMergeFlagAttr() ); 2775 2776 rDoc.ApplyPatternArea( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, aMark, aPattern ); 2777 2778 for (const auto& rTab : aMark) 2779 { 2780 if (rTab >= nTabCount) 2781 break; 2782 2783 SCTAB nScenarioCount = 0; 2784 2785 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2786 nScenarioCount ++; 2787 2788 ScRange aMergedRange( nExtendStartCol, nExtendStartRow, rTab, nMergeTestEndCol, nMergeTestEndRow, rTab+nScenarioCount ); 2789 rDoc.ExtendMerge( aMergedRange, true ); 2790 } 2791 } 2792 2793 for (const auto& rTab : aMark) 2794 { 2795 if (rTab >= nTabCount) 2796 break; 2797 2798 rDoc.RefreshAutoFilter( nExtendStartCol, nExtendStartRow, nMergeTestEndCol, nMergeTestEndRow, rTab ); 2799 } 2800 2801 for (const auto& rTab : aMark) 2802 { 2803 if (rTab >= nTabCount) 2804 break; 2805 2806 rDoc.SetDrawPageSize(rTab); 2807 2808 if ( eCmd == DelCellCmd::Cols || eCmd == DelCellCmd::Rows ) 2809 rDoc.UpdatePageBreaks( rTab ); 2810 2811 rDocShell.UpdatePaintExt( nExtFlags, nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab ); 2812 2813 SCTAB nScenarioCount = 0; 2814 2815 for( SCTAB j = rTab+1; j<nTabCount && rDoc.IsScenario(j); j++ ) 2816 nScenarioCount ++; 2817 2818 // delete entire rows: do not adjust 2819 if ( eCmd == DelCellCmd::Rows || !AdjustRowHeight(ScRange( 0, nPaintStartRow, rTab, rDoc.MaxCol(), nPaintEndRow, rTab+nScenarioCount ), true, bApi) ) 2820 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, nPaintFlags, nExtFlags ); 2821 else 2822 { 2823 // paint only what is not done by AdjustRowHeight 2824 if (nExtFlags & SC_PF_LINES) 2825 lcl_PaintAbove( rDocShell, ScRange( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount) ); 2826 if (nPaintFlags & PaintPartFlags::Top) 2827 rDocShell.PostPaint( nPaintStartCol, nPaintStartRow, rTab, nPaintEndCol, nPaintEndRow, rTab+nScenarioCount, PaintPartFlags::Top ); 2828 } 2829 } 2830 2831 // The cursor position needs to be modified earlier than updating 2832 // any enabled edit view which is triggered by SetDocumentModified below. 2833 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); 2834 if (pViewSh) 2835 { 2836 if (eCmd == DelCellCmd::Cols) 2837 { 2838 pViewSh->OnLOKInsertDeleteColumn(rRange.aStart.Col(), -1); 2839 } 2840 if (eCmd == DelCellCmd::Rows) 2841 { 2842 pViewSh->OnLOKInsertDeleteRow(rRange.aStart.Row(), -1); 2843 } 2844 } 2845 2846 aModificator.SetDocumentModified(); 2847 2848 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); 2849 2850 return true; 2851 } 2852 2853 bool ScDocFunc::MoveBlock( const ScRange& rSource, const ScAddress& rDestPos, 2854 bool bCut, bool bRecord, bool bPaint, bool bApi ) 2855 { 2856 ScDocShellModificator aModificator( rDocShell ); 2857 2858 SCCOL nStartCol = rSource.aStart.Col(); 2859 SCROW nStartRow = rSource.aStart.Row(); 2860 SCTAB nStartTab = rSource.aStart.Tab(); 2861 SCCOL nEndCol = rSource.aEnd.Col(); 2862 SCROW nEndRow = rSource.aEnd.Row(); 2863 SCTAB nEndTab = rSource.aEnd.Tab(); 2864 SCCOL nDestCol = rDestPos.Col(); 2865 SCROW nDestRow = rDestPos.Row(); 2866 SCTAB nDestTab = rDestPos.Tab(); 2867 2868 ScDocument& rDoc = rDocShell.GetDocument(); 2869 if ( !rDoc.ValidRow(nStartRow) || !rDoc.ValidRow(nEndRow) || !rDoc.ValidRow(nDestRow) ) 2870 { 2871 OSL_FAIL("invalid row in MoveBlock"); 2872 return false; 2873 } 2874 2875 // adjust related scenarios too - but only when moved within one sheet 2876 bool bScenariosAdded = false; 2877 if (bRecord && !rDoc.IsUndoEnabled()) 2878 bRecord = false; 2879 2880 SCTAB nTabCount = rDoc.GetTableCount(); 2881 if ( nDestTab == nStartTab && !rDoc.IsScenario(nEndTab) ) 2882 while ( nEndTab+1 < nTabCount && rDoc.IsScenario(nEndTab+1) ) 2883 { 2884 ++nEndTab; 2885 bScenariosAdded = true; 2886 } 2887 2888 SCTAB nSrcTabCount = nEndTab-nStartTab+1; 2889 SCTAB nDestEndTab = nDestTab+nSrcTabCount-1; 2890 SCTAB nTab; 2891 2892 ScDocumentUniquePtr pClipDoc(new ScDocument(SCDOCMODE_CLIP)); 2893 2894 ScMarkData aSourceMark(rDoc.GetSheetLimits()); 2895 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 2896 aSourceMark.SelectTable( nTab, true ); // select source 2897 aSourceMark.SetMarkArea( rSource ); 2898 2899 ScDocShellRef aDragShellRef; 2900 if ( rDoc.HasOLEObjectsInArea( rSource ) ) 2901 { 2902 aDragShellRef = new ScDocShell; // DocShell needs a Ref immediately 2903 aDragShellRef->DoInitNew(); 2904 } 2905 ScDrawLayer::SetGlobalDrawPersist( aDragShellRef.get() ); 2906 2907 ScClipParam aClipParam(ScRange(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nStartTab), bCut); 2908 rDoc.CopyToClip(aClipParam, pClipDoc.get(), &aSourceMark, bScenariosAdded, true); 2909 2910 ScDrawLayer::SetGlobalDrawPersist(nullptr); 2911 2912 SCCOL nOldEndCol = nEndCol; 2913 SCROW nOldEndRow = nEndRow; 2914 bool bClipOver = false; 2915 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 2916 { 2917 SCCOL nTmpEndCol = nOldEndCol; 2918 SCROW nTmpEndRow = nOldEndRow; 2919 if (rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab )) 2920 bClipOver = true; 2921 if ( nTmpEndCol > nEndCol ) nEndCol = nTmpEndCol; 2922 if ( nTmpEndRow > nEndRow ) nEndRow = nTmpEndRow; 2923 } 2924 2925 SCCOL nDestEndCol = nDestCol + ( nOldEndCol-nStartCol ); 2926 SCROW nDestEndRow = nDestRow + ( nOldEndRow-nStartRow ); 2927 2928 SCCOL nUndoEndCol = nDestCol + ( nEndCol-nStartCol ); // extended in destination block 2929 SCROW nUndoEndRow = nDestRow + ( nEndRow-nStartRow ); 2930 2931 bool bIncludeFiltered = bCut; 2932 if ( !bIncludeFiltered ) 2933 { 2934 // adjust sizes to include only non-filtered rows 2935 2936 SCCOL nClipX; 2937 SCROW nClipY; 2938 pClipDoc->GetClipArea( nClipX, nClipY, false ); 2939 SCROW nUndoAdd = nUndoEndRow - nDestEndRow; 2940 nDestEndRow = nDestRow + nClipY; 2941 nUndoEndRow = nDestEndRow + nUndoAdd; 2942 } 2943 2944 if (!rDoc.ValidCol(nUndoEndCol) || !rDoc.ValidRow(nUndoEndRow)) 2945 { 2946 if (!bApi) 2947 rDocShell.ErrorMessage(STR_PASTE_FULL); 2948 return false; 2949 } 2950 2951 // Test for cell protection 2952 2953 ScEditableTester aTester; 2954 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++) 2955 aTester.TestBlock( rDoc, nTab, nDestCol,nDestRow, nUndoEndCol,nUndoEndRow ); 2956 if (bCut) 2957 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 2958 aTester.TestBlock( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow ); 2959 2960 if (!aTester.IsEditable()) 2961 { 2962 if (!bApi) 2963 rDocShell.ErrorMessage(aTester.GetMessageId()); 2964 return false; 2965 } 2966 2967 // Test for merged cells- when moving after delete 2968 2969 if (bClipOver && !bCut) 2970 if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, nUndoEndCol,nUndoEndRow,nDestEndTab, 2971 HasAttrFlags::Merged | HasAttrFlags::Overlapped )) 2972 { // "Merge of already merged cells not possible" 2973 if (!bApi) 2974 rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0); 2975 return false; 2976 } 2977 2978 // Are there borders in the cells? (for painting) 2979 2980 sal_uInt16 nSourceExt = 0; 2981 rDocShell.UpdatePaintExt( nSourceExt, nStartCol,nStartRow,nStartTab, nEndCol,nEndRow,nEndTab ); 2982 sal_uInt16 nDestExt = 0; 2983 rDocShell.UpdatePaintExt( nDestExt, nDestCol,nDestRow,nDestTab, nDestEndCol,nDestEndRow,nDestEndTab ); 2984 2985 // do it 2986 2987 ScDocumentUniquePtr pUndoDoc; 2988 2989 if (bRecord) 2990 { 2991 bool bWholeCols = ( nStartRow == 0 && nEndRow == rDoc.MaxRow() ); 2992 bool bWholeRows = ( nStartCol == 0 && nEndCol == rDoc.MaxCol() ); 2993 InsertDeleteFlags nUndoFlags = (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS; 2994 2995 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 2996 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab, bWholeCols, bWholeRows ); 2997 2998 if (bCut) 2999 { 3000 rDoc.CopyToDocument( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, 3001 nUndoFlags, false, *pUndoDoc ); 3002 } 3003 3004 if ( nDestTab != nStartTab ) 3005 pUndoDoc->AddUndoTab( nDestTab, nDestEndTab, bWholeCols, bWholeRows ); 3006 rDoc.CopyToDocument( nDestCol, nDestRow, nDestTab, 3007 nDestEndCol, nDestEndRow, nDestEndTab, 3008 nUndoFlags, false, *pUndoDoc ); 3009 rDoc.BeginDrawUndo(); 3010 } 3011 3012 bool bSourceHeight = false; // adjust heights? 3013 if (bCut) 3014 { 3015 ScMarkData aDelMark(rDoc.GetSheetLimits()); // only for tables 3016 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 3017 { 3018 rDoc.DeleteAreaTab( nStartCol,nStartRow, nOldEndCol,nOldEndRow, nTab, InsertDeleteFlags::ALL ); 3019 aDelMark.SelectTable( nTab, true ); 3020 } 3021 rDoc.DeleteObjectsInArea( nStartCol,nStartRow, nOldEndCol,nOldEndRow, aDelMark ); 3022 3023 // Test for merged cells 3024 3025 if (bClipOver) 3026 if (rDoc.HasAttrib( nDestCol,nDestRow,nDestTab, 3027 nUndoEndCol,nUndoEndRow,nDestEndTab, 3028 HasAttrFlags::Merged | HasAttrFlags::Overlapped )) 3029 { 3030 rDoc.CopyFromClip( rSource, aSourceMark, InsertDeleteFlags::ALL, nullptr, pClipDoc.get() ); 3031 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 3032 { 3033 SCCOL nTmpEndCol = nEndCol; 3034 SCROW nTmpEndRow = nEndRow; 3035 rDoc.ExtendMerge( nStartCol, nStartRow, nTmpEndCol, nTmpEndRow, nTab, true ); 3036 } 3037 3038 // Report error only after restoring content 3039 if (!bApi) // "Merge of already merged cells not possible" 3040 rDocShell.ErrorMessage(STR_MSSG_MOVEBLOCKTO_0); 3041 3042 return false; 3043 } 3044 3045 bSourceHeight = AdjustRowHeight( rSource, false, bApi ); 3046 } 3047 3048 ScRange aPasteDest( nDestCol, nDestRow, nDestTab, nDestEndCol, nDestEndRow, nDestEndTab ); 3049 3050 ScMarkData aDestMark(rDoc.GetSheetLimits()); 3051 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++) 3052 aDestMark.SelectTable( nTab, true ); // select destination 3053 aDestMark.SetMarkArea( aPasteDest ); 3054 3055 /* Do not copy drawing objects here. While pasting, the 3056 function ScDocument::UpdateReference() is called which calls 3057 ScDrawLayer::MoveCells() which may move away inserted objects to wrong 3058 positions (e.g. if source and destination range overlaps).*/ 3059 3060 rDoc.CopyFromClip( 3061 aPasteDest, aDestMark, InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS, 3062 pUndoDoc.get(), pClipDoc.get(), true, false, bIncludeFiltered); 3063 3064 // skipped rows and merged cells don't mix 3065 if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() ) 3066 UnmergeCells( aPasteDest, false, nullptr ); 3067 3068 bool bDestHeight = AdjustRowHeight( 3069 ScRange( 0,nDestRow,nDestTab, rDoc.MaxCol(),nDestEndRow,nDestEndTab ), 3070 false, bApi ); 3071 3072 /* Paste drawing objects after adjusting formula references 3073 and row heights. There are no cell notes or drawing objects, if the 3074 clipdoc does not contain a drawing layer.*/ 3075 if ( pClipDoc->GetDrawLayer() ) 3076 rDoc.CopyFromClip( aPasteDest, aDestMark, InsertDeleteFlags::OBJECTS, 3077 nullptr, pClipDoc.get(), true, false, bIncludeFiltered ); 3078 3079 if (bRecord) 3080 { 3081 ScRange aUndoRange(nStartCol, nStartRow, nStartTab, nOldEndCol, nOldEndRow, nEndTab); 3082 ScAddress aDestPos(nDestCol, nDestRow, nDestTab); 3083 3084 rDocShell.GetUndoManager()->AddUndoAction( 3085 std::make_unique<ScUndoDragDrop>( 3086 &rDocShell, aUndoRange, aDestPos, bCut, std::move(pUndoDoc), bScenariosAdded)); 3087 } 3088 3089 SCCOL nDestPaintEndCol = nDestEndCol; 3090 SCROW nDestPaintEndRow = nDestEndRow; 3091 for (nTab=nDestTab; nTab<=nDestEndTab; nTab++) 3092 { 3093 SCCOL nTmpEndCol = nDestEndCol; 3094 SCROW nTmpEndRow = nDestEndRow; 3095 rDoc.ExtendMerge( nDestCol, nDestRow, nTmpEndCol, nTmpEndRow, nTab, true ); 3096 if (nTmpEndCol > nDestPaintEndCol) nDestPaintEndCol = nTmpEndCol; 3097 if (nTmpEndRow > nDestPaintEndRow) nDestPaintEndRow = nTmpEndRow; 3098 } 3099 3100 if (bCut) 3101 for (nTab=nStartTab; nTab<=nEndTab; nTab++) 3102 rDoc.RefreshAutoFilter( nStartCol, nStartRow, nEndCol, nEndRow, nTab ); 3103 3104 if (bPaint) 3105 { 3106 // destination range: 3107 3108 SCCOL nPaintStartX = nDestCol; 3109 SCROW nPaintStartY = nDestRow; 3110 SCCOL nPaintEndX = nDestPaintEndCol; 3111 SCROW nPaintEndY = nDestPaintEndRow; 3112 PaintPartFlags nFlags = PaintPartFlags::Grid; 3113 3114 if ( nStartRow==0 && nEndRow==rDoc.MaxRow() ) // copy widths too? 3115 { 3116 nPaintEndX = rDoc.MaxCol(); 3117 nPaintStartY = 0; 3118 nPaintEndY = rDoc.MaxRow(); 3119 nFlags |= PaintPartFlags::Top; 3120 } 3121 if ( bDestHeight || ( nStartCol == 0 && nEndCol == rDoc.MaxCol() ) ) 3122 { 3123 nPaintEndY = rDoc.MaxRow(); 3124 nPaintStartX = 0; 3125 nPaintEndX = rDoc.MaxCol(); 3126 nFlags |= PaintPartFlags::Left; 3127 } 3128 if ( bScenariosAdded ) 3129 { 3130 nPaintStartX = 0; 3131 nPaintStartY = 0; 3132 nPaintEndX = rDoc.MaxCol(); 3133 nPaintEndY = rDoc.MaxRow(); 3134 } 3135 3136 rDocShell.PostPaint( nPaintStartX,nPaintStartY,nDestTab, 3137 nPaintEndX,nPaintEndY,nDestEndTab, nFlags, nSourceExt | nDestExt ); 3138 3139 if ( bCut ) 3140 { 3141 // source range: 3142 3143 nPaintStartX = nStartCol; 3144 nPaintStartY = nStartRow; 3145 nPaintEndX = nEndCol; 3146 nPaintEndY = nEndRow; 3147 nFlags = PaintPartFlags::Grid; 3148 3149 if ( bSourceHeight ) 3150 { 3151 nPaintEndY = rDoc.MaxRow(); 3152 nPaintStartX = 0; 3153 nPaintEndX = rDoc.MaxCol(); 3154 nFlags |= PaintPartFlags::Left; 3155 } 3156 if ( bScenariosAdded ) 3157 { 3158 nPaintStartX = 0; 3159 nPaintStartY = 0; 3160 nPaintEndX = rDoc.MaxCol(); 3161 nPaintEndY = rDoc.MaxRow(); 3162 } 3163 3164 rDocShell.PostPaint( nPaintStartX,nPaintStartY,nStartTab, 3165 nPaintEndX,nPaintEndY,nEndTab, nFlags, nSourceExt ); 3166 } 3167 } 3168 3169 aModificator.SetDocumentModified(); 3170 3171 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); 3172 3173 return true; 3174 } 3175 3176 static uno::Reference< uno::XInterface > GetDocModuleObject( const SfxObjectShell& rDocSh, const OUString& sCodeName ) 3177 { 3178 uno::Reference< lang::XMultiServiceFactory> xSF(rDocSh.GetModel(), uno::UNO_QUERY); 3179 uno::Reference< container::XNameAccess > xVBACodeNamedObjectAccess; 3180 uno::Reference< uno::XInterface > xDocModuleApiObject; 3181 if ( xSF.is() ) 3182 { 3183 xVBACodeNamedObjectAccess.set( xSF->createInstance("ooo.vba.VBAObjectModuleObjectProvider"), uno::UNO_QUERY ); 3184 xDocModuleApiObject.set( xVBACodeNamedObjectAccess->getByName( sCodeName ), uno::UNO_QUERY ); 3185 } 3186 return xDocModuleApiObject; 3187 3188 } 3189 3190 static script::ModuleInfo lcl_InitModuleInfo( const SfxObjectShell& rDocSh, const OUString& sModule ) 3191 { 3192 script::ModuleInfo sModuleInfo; 3193 sModuleInfo.ModuleType = script::ModuleType::DOCUMENT; 3194 sModuleInfo.ModuleObject = GetDocModuleObject( rDocSh, sModule ); 3195 return sModuleInfo; 3196 } 3197 3198 void VBA_InsertModule( ScDocument& rDoc, SCTAB nTab, const OUString& sSource ) 3199 { 3200 ScDocShell& rDocSh = *rDoc.GetDocumentShell(); 3201 uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer(); 3202 OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" ); 3203 3204 uno::Reference< container::XNameContainer > xLib; 3205 if( xLibContainer.is() ) 3206 { 3207 OUString aLibName( "Standard" ); 3208 #if HAVE_FEATURE_SCRIPTING 3209 if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() ) 3210 { 3211 aLibName = rDocSh.GetBasicManager()->GetName(); 3212 } 3213 #endif 3214 uno::Any aLibAny = xLibContainer->getByName( aLibName ); 3215 aLibAny >>= xLib; 3216 } 3217 if( !xLib.is() ) 3218 return; 3219 3220 // if the Module with codename exists then find a new name 3221 sal_Int32 nNum = 1; 3222 OUString genModuleName = "Sheet1"; 3223 while( xLib->hasByName( genModuleName ) ) 3224 genModuleName = "Sheet" + OUString::number( ++nNum ); 3225 3226 uno::Any aSourceAny; 3227 OUString sTmpSource = sSource; 3228 if ( sTmpSource.isEmpty() ) 3229 sTmpSource = "Rem Attribute VBA_ModuleType=VBADocumentModule\nOption VBASupport 1\n"; 3230 aSourceAny <<= sTmpSource; 3231 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY ); 3232 if ( xVBAModuleInfo.is() ) 3233 { 3234 rDoc.SetCodeName( nTab, genModuleName ); 3235 script::ModuleInfo sModuleInfo = lcl_InitModuleInfo( rDocSh, genModuleName ); 3236 xVBAModuleInfo->insertModuleInfo( genModuleName, sModuleInfo ); 3237 xLib->insertByName( genModuleName, aSourceAny ); 3238 } 3239 } 3240 3241 void VBA_DeleteModule( ScDocShell& rDocSh, const OUString& sModuleName ) 3242 { 3243 uno::Reference< script::XLibraryContainer > xLibContainer = rDocSh.GetBasicContainer(); 3244 OSL_ENSURE( xLibContainer.is(), "No BasicContainer!" ); 3245 3246 uno::Reference< container::XNameContainer > xLib; 3247 if( xLibContainer.is() ) 3248 { 3249 OUString aLibName( "Standard" ); 3250 #if HAVE_FEATURE_SCRIPTING 3251 if ( rDocSh.GetBasicManager() && !rDocSh.GetBasicManager()->GetName().isEmpty() ) 3252 { 3253 aLibName = rDocSh.GetBasicManager()->GetName(); 3254 } 3255 #endif 3256 uno::Any aLibAny = xLibContainer->getByName( aLibName ); 3257 aLibAny >>= xLib; 3258 } 3259 if( xLib.is() ) 3260 { 3261 uno::Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, uno::UNO_QUERY ); 3262 if( xLib->hasByName( sModuleName ) ) 3263 xLib->removeByName( sModuleName ); 3264 if ( xVBAModuleInfo.is() && xVBAModuleInfo->hasModuleInfo(sModuleName) ) 3265 xVBAModuleInfo->removeModuleInfo( sModuleName ); 3266 3267 } 3268 } 3269 3270 bool ScDocFunc::InsertTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi ) 3271 { 3272 bool bSuccess = false; 3273 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 3274 3275 ScDocShellModificator aModificator( rDocShell ); 3276 3277 ScDocument& rDoc = rDocShell.GetDocument(); 3278 3279 // Strange loop, also basic is loaded too early ( InsertTable ) 3280 // is called via the xml import for sheets in described in ODF 3281 bool bInsertDocModule = false; 3282 3283 if( !rDocShell.GetDocument().IsImportingXML() ) 3284 { 3285 bInsertDocModule = rDoc.IsInVBAMode(); 3286 } 3287 if ( bInsertDocModule || ( bRecord && !rDoc.IsUndoEnabled() ) ) 3288 bRecord = false; 3289 3290 if (bRecord) 3291 rDoc.BeginDrawUndo(); // InsertTab generates SdrUndoNewPage 3292 3293 SCTAB nTabCount = rDoc.GetTableCount(); 3294 bool bAppend = ( nTab >= nTabCount ); 3295 if ( bAppend ) 3296 nTab = nTabCount; // important for Undo 3297 3298 if (rDoc.InsertTab( nTab, rName )) 3299 { 3300 if (bRecord) 3301 rDocShell.GetUndoManager()->AddUndoAction( 3302 std::make_unique<ScUndoInsertTab>( &rDocShell, nTab, bAppend, rName)); 3303 // Update views: 3304 // Only insert vba modules if vba mode ( and not currently importing XML ) 3305 if( bInsertDocModule ) 3306 { 3307 VBA_InsertModule( rDoc, nTab, OUString() ); 3308 } 3309 rDocShell.Broadcast( ScTablesHint( SC_TAB_INSERTED, nTab ) ); 3310 3311 rDocShell.PostPaintExtras(); 3312 aModificator.SetDocumentModified(); 3313 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); 3314 bSuccess = true; 3315 } 3316 else if (!bApi) 3317 rDocShell.ErrorMessage(STR_TABINSERT_ERROR); 3318 3319 return bSuccess; 3320 } 3321 3322 bool ScDocFunc::DeleteTable( SCTAB nTab, bool bRecord ) 3323 { 3324 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 3325 3326 ScDocShellModificator aModificator( rDocShell ); 3327 3328 bool bSuccess = false; 3329 ScDocument& rDoc = rDocShell.GetDocument(); 3330 bool bVbaEnabled = rDoc.IsInVBAMode(); 3331 if (bRecord && !rDoc.IsUndoEnabled()) 3332 bRecord = false; 3333 if ( bVbaEnabled ) 3334 bRecord = false; 3335 bool bWasLinked = rDoc.IsLinked(nTab); 3336 ScDocumentUniquePtr pUndoDoc; 3337 std::unique_ptr<ScRefUndoData> pUndoData; 3338 if (bRecord) 3339 { 3340 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 3341 SCTAB nCount = rDoc.GetTableCount(); 3342 3343 pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); // only nTab with Flags 3344 pUndoDoc->AddUndoTab( 0, nCount-1 ); // all sheets for references 3345 3346 rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(),rDoc.MaxRow(),nTab, InsertDeleteFlags::ALL,false, *pUndoDoc ); 3347 OUString aOldName; 3348 rDoc.GetName( nTab, aOldName ); 3349 pUndoDoc->RenameTab( nTab, aOldName ); 3350 if (bWasLinked) 3351 pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab), 3352 rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab), 3353 rDoc.GetLinkTab(nTab), 3354 rDoc.GetLinkRefreshDelay(nTab) ); 3355 3356 if ( rDoc.IsScenario(nTab) ) 3357 { 3358 pUndoDoc->SetScenario( nTab, true ); 3359 OUString aComment; 3360 Color aColor; 3361 ScScenarioFlags nScenFlags; 3362 rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags ); 3363 pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags ); 3364 bool bActive = rDoc.IsActiveScenario( nTab ); 3365 pUndoDoc->SetActiveScenario( nTab, bActive ); 3366 } 3367 pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) ); 3368 pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) ); 3369 auto pSheetEvents = rDoc.GetSheetEvents( nTab ); 3370 pUndoDoc->SetSheetEvents( nTab, std::unique_ptr<ScSheetEvents>(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) ); 3371 3372 // Drawing-Layer has to take care of its own undo!!! 3373 rDoc.BeginDrawUndo(); // DeleteTab generates SdrUndoDelPage 3374 3375 pUndoData.reset(new ScRefUndoData( &rDoc )); 3376 } 3377 3378 if (rDoc.DeleteTab(nTab)) 3379 { 3380 if (bRecord) 3381 { 3382 vector<SCTAB> theTabs; 3383 theTabs.push_back(nTab); 3384 rDocShell.GetUndoManager()->AddUndoAction( 3385 std::make_unique<ScUndoDeleteTab>( &rDocShell, theTabs, std::move(pUndoDoc), std::move(pUndoData) )); 3386 } 3387 // Update views: 3388 if( bVbaEnabled ) 3389 { 3390 OUString sCodeName; 3391 if( rDoc.GetCodeName( nTab, sCodeName ) ) 3392 { 3393 VBA_DeleteModule( rDocShell, sCodeName ); 3394 } 3395 } 3396 rDocShell.Broadcast( ScTablesHint( SC_TAB_DELETED, nTab ) ); 3397 3398 if (bWasLinked) 3399 { 3400 rDocShell.UpdateLinks(); // update Link-Manager 3401 SfxBindings* pBindings = rDocShell.GetViewBindings(); 3402 if (pBindings) 3403 pBindings->Invalidate(SID_LINKS); 3404 } 3405 3406 rDocShell.PostPaintExtras(); 3407 aModificator.SetDocumentModified(); 3408 3409 SfxApplication* pSfxApp = SfxGetpApp(); // Navigator 3410 pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); 3411 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) ); 3412 pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); 3413 pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); 3414 3415 bSuccess = true; 3416 } 3417 return bSuccess; 3418 } 3419 3420 void ScDocFunc::SetTableVisible( SCTAB nTab, bool bVisible, bool bApi ) 3421 { 3422 ScDocument& rDoc = rDocShell.GetDocument(); 3423 bool bUndo(rDoc.IsUndoEnabled()); 3424 if ( rDoc.IsVisible( nTab ) == bVisible ) 3425 return; // nothing to do - ok 3426 3427 if ( !rDoc.IsDocEditable() ) 3428 { 3429 if (!bApi) 3430 rDocShell.ErrorMessage(STR_PROTECTIONERR); 3431 return; 3432 } 3433 3434 ScDocShellModificator aModificator( rDocShell ); 3435 3436 if ( !bVisible && !rDoc.IsImportingXML() ) // #i57869# allow hiding in any order for loading 3437 { 3438 // do not disable all sheets 3439 3440 sal_uInt16 nVisCount = 0; 3441 SCTAB nCount = rDoc.GetTableCount(); 3442 for (SCTAB i=0; i<nCount && nVisCount<2; i++) 3443 if (rDoc.IsVisible(i)) 3444 ++nVisCount; 3445 3446 if (nVisCount <= 1) 3447 { 3448 if (!bApi) 3449 rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message? 3450 return; 3451 } 3452 } 3453 3454 rDoc.SetVisible( nTab, bVisible ); 3455 if (bUndo) 3456 { 3457 std::vector<SCTAB> undoTabs { nTab }; 3458 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoShowHideTab>( &rDocShell, std::move(undoTabs), bVisible ) ); 3459 } 3460 3461 // update views 3462 if (!bVisible) 3463 rDocShell.Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) ); 3464 3465 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); 3466 rDocShell.PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras); 3467 aModificator.SetDocumentModified(); 3468 } 3469 3470 bool ScDocFunc::SetLayoutRTL( SCTAB nTab, bool bRTL ) 3471 { 3472 ScDocument& rDoc = rDocShell.GetDocument(); 3473 bool bUndo(rDoc.IsUndoEnabled()); 3474 if ( rDoc.IsLayoutRTL( nTab ) == bRTL ) 3475 return true; // nothing to do - ok 3476 3477 //! protection (sheet or document?) 3478 3479 ScDocShellModificator aModificator( rDocShell ); 3480 3481 rDoc.SetLayoutRTL( nTab, bRTL, ScObjectHandling::MirrorRTLMode); 3482 3483 if (bUndo) 3484 { 3485 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoLayoutRTL>( &rDocShell, nTab, bRTL ) ); 3486 } 3487 3488 rDocShell.PostPaint( 0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab, PaintPartFlags::All ); 3489 aModificator.SetDocumentModified(); 3490 3491 SfxBindings* pBindings = rDocShell.GetViewBindings(); 3492 if (pBindings) 3493 { 3494 pBindings->Invalidate( FID_TAB_RTL ); 3495 pBindings->Invalidate( SID_ATTR_SIZE ); 3496 } 3497 3498 return true; 3499 } 3500 3501 bool ScDocFunc::RenameTable( SCTAB nTab, const OUString& rName, bool bRecord, bool bApi ) 3502 { 3503 ScDocument& rDoc = rDocShell.GetDocument(); 3504 if (bRecord && !rDoc.IsUndoEnabled()) 3505 bRecord = false; 3506 if ( !rDoc.IsDocEditable() ) 3507 { 3508 if (!bApi) 3509 rDocShell.ErrorMessage(STR_PROTECTIONERR); 3510 return false; 3511 } 3512 3513 ScDocShellModificator aModificator( rDocShell ); 3514 3515 bool bSuccess = false; 3516 OUString sOldName; 3517 rDoc.GetName(nTab, sOldName); 3518 if (rDoc.RenameTab( nTab, rName )) 3519 { 3520 if (bRecord) 3521 { 3522 rDocShell.GetUndoManager()->AddUndoAction( 3523 std::make_unique<ScUndoRenameTab>( &rDocShell, nTab, sOldName, rName)); 3524 } 3525 rDocShell.PostPaintExtras(); 3526 aModificator.SetDocumentModified(); 3527 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); 3528 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) ); 3529 3530 bSuccess = true; 3531 } 3532 return bSuccess; 3533 } 3534 3535 bool ScDocFunc::SetTabBgColor( SCTAB nTab, const Color& rColor, bool bRecord, bool bApi ) 3536 { 3537 3538 ScDocument& rDoc = rDocShell.GetDocument(); 3539 if (bRecord && !rDoc.IsUndoEnabled()) 3540 bRecord = false; 3541 if ( !rDoc.IsDocEditable() || rDoc.IsTabProtected(nTab) ) 3542 { 3543 if (!bApi) 3544 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Check to see what this string is... 3545 return false; 3546 } 3547 3548 Color aOldTabBgColor = rDoc.GetTabBgColor(nTab); 3549 3550 bool bSuccess = false; 3551 rDoc.SetTabBgColor(nTab, rColor); 3552 if ( rDoc.GetTabBgColor(nTab) == rColor) 3553 bSuccess = true; 3554 if (bSuccess) 3555 { 3556 if (bRecord) 3557 { 3558 rDocShell.GetUndoManager()->AddUndoAction( 3559 std::make_unique<ScUndoTabColor>( &rDocShell, nTab, aOldTabBgColor, rColor)); 3560 } 3561 rDocShell.PostPaintExtras(); 3562 ScDocShellModificator aModificator( rDocShell ); 3563 aModificator.SetDocumentModified(); 3564 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); 3565 3566 bSuccess = true; 3567 } 3568 return bSuccess; 3569 } 3570 3571 bool ScDocFunc::SetTabBgColor( 3572 ScUndoTabColorInfo::List& rUndoTabColorList, bool bApi ) 3573 { 3574 ScDocument& rDoc = rDocShell.GetDocument(); 3575 bool bRecord = true; 3576 if (!rDoc.IsUndoEnabled()) 3577 bRecord = false; 3578 3579 if ( !rDoc.IsDocEditable() ) 3580 { 3581 if (!bApi) 3582 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error... 3583 return false; 3584 } 3585 3586 sal_uInt16 nTab; 3587 Color aNewTabBgColor; 3588 bool bSuccess = true; 3589 size_t nTabProtectCount = 0; 3590 size_t nTabListCount = rUndoTabColorList.size(); 3591 for ( size_t i = 0; i < nTabListCount; ++i ) 3592 { 3593 ScUndoTabColorInfo& rInfo = rUndoTabColorList[i]; 3594 nTab = rInfo.mnTabId; 3595 if ( !rDoc.IsTabProtected(nTab) ) 3596 { 3597 aNewTabBgColor = rInfo.maNewTabBgColor; 3598 rInfo.maOldTabBgColor = rDoc.GetTabBgColor(nTab); 3599 rDoc.SetTabBgColor(nTab, aNewTabBgColor); 3600 if ( rDoc.GetTabBgColor(nTab) != aNewTabBgColor) 3601 { 3602 bSuccess = false; 3603 break; 3604 } 3605 } 3606 else 3607 { 3608 nTabProtectCount++; 3609 } 3610 } 3611 3612 if ( nTabProtectCount == nTabListCount ) 3613 { 3614 if (!bApi) 3615 rDocShell.ErrorMessage(STR_PROTECTIONERR); //TODO Get a better String Error... 3616 return false; 3617 } 3618 3619 if (bSuccess) 3620 { 3621 if (bRecord) 3622 { 3623 rDocShell.GetUndoManager()->AddUndoAction( 3624 std::make_unique<ScUndoTabColor>( &rDocShell, std::vector(rUndoTabColorList))); 3625 } 3626 rDocShell.PostPaintExtras(); 3627 ScDocShellModificator aModificator( rDocShell ); 3628 aModificator.SetDocumentModified(); 3629 } 3630 return bSuccess; 3631 } 3632 3633 //! SetWidthOrHeight - duplicated in ViewFunc !!!!!! 3634 //! Problems: 3635 //! - Optimal height of text cells is different for a printer and a screen 3636 //! - Optimal width needs a selection in order to take only selected cells into account 3637 3638 static sal_uInt16 lcl_GetOptimalColWidth( ScDocShell& rDocShell, SCCOL nCol, SCTAB nTab ) 3639 { 3640 ScSizeDeviceProvider aProv(&rDocShell); 3641 OutputDevice* pDev = aProv.GetDevice(); // has pixel MapMode 3642 double nPPTX = aProv.GetPPTX(); 3643 double nPPTY = aProv.GetPPTY(); 3644 3645 ScDocument& rDoc = rDocShell.GetDocument(); 3646 Fraction aOne(1,1); 3647 sal_uInt16 nTwips = rDoc.GetOptimalColWidth( nCol, nTab, pDev, nPPTX, nPPTY, aOne, aOne, 3648 false/*bFormula*/ ); 3649 3650 return nTwips; 3651 } 3652 3653 bool ScDocFunc::SetWidthOrHeight( 3654 bool bWidth, const std::vector<sc::ColRowSpan>& rRanges, SCTAB nTab, 3655 ScSizeMode eMode, sal_uInt16 nSizeTwips, bool bRecord, bool bApi ) 3656 { 3657 ScDocShellModificator aModificator( rDocShell ); 3658 3659 if (rRanges.empty()) 3660 return true; 3661 3662 ScDocument& rDoc = rDocShell.GetDocument(); 3663 if ( bRecord && !rDoc.IsUndoEnabled() ) 3664 bRecord = false; 3665 3666 // import into read-only document is possible 3667 if ( !rDoc.IsChangeReadOnlyEnabled() && !rDocShell.IsEditable() ) 3668 { 3669 if (!bApi) 3670 rDocShell.ErrorMessage(STR_PROTECTIONERR); //! separate error message? 3671 return false; 3672 } 3673 3674 SCCOLROW nStart = rRanges[0].mnStart; 3675 SCCOLROW nEnd = rRanges[0].mnEnd; 3676 3677 if ( eMode == SC_SIZE_OPTIMAL ) 3678 { 3679 //! Option "Show formulas" - but where to get them from? 3680 } 3681 3682 ScDocumentUniquePtr pUndoDoc; 3683 std::unique_ptr<ScOutlineTable> pUndoTab; 3684 std::vector<sc::ColRowSpan> aUndoRanges; 3685 3686 if ( bRecord ) 3687 { 3688 rDoc.BeginDrawUndo(); // Drawing Updates 3689 3690 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 3691 if (bWidth) 3692 { 3693 pUndoDoc->InitUndo( rDoc, nTab, nTab, true ); 3694 rDoc.CopyToDocument( static_cast<SCCOL>(nStart), 0, nTab, static_cast<SCCOL>(nEnd), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc ); 3695 } 3696 else 3697 { 3698 pUndoDoc->InitUndo( rDoc, nTab, nTab, false, true ); 3699 rDoc.CopyToDocument( 0, static_cast<SCROW>(nStart), nTab, rDoc.MaxCol(), static_cast<SCROW>(nEnd), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc ); 3700 } 3701 3702 aUndoRanges = rRanges; 3703 3704 ScOutlineTable* pTable = rDoc.GetOutlineTable( nTab ); 3705 if (pTable) 3706 pUndoTab.reset(new ScOutlineTable( *pTable )); 3707 } 3708 3709 bool bShow = nSizeTwips > 0 || eMode != SC_SIZE_DIRECT; 3710 bool bOutline = false; 3711 3712 for (const sc::ColRowSpan& rRange : rRanges) 3713 { 3714 SCCOLROW nStartNo = rRange.mnStart; 3715 SCCOLROW nEndNo = rRange.mnEnd; 3716 3717 if ( !bWidth ) // deal with heights always in blocks 3718 { 3719 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT ) 3720 { 3721 bool bAll = ( eMode==SC_SIZE_OPTIMAL ); 3722 if (!bAll) 3723 { 3724 // delete for all that have CRFlags::ManualSize enabled 3725 // then SetOptimalHeight with bShrink = FALSE 3726 for (SCROW nRow=nStartNo; nRow<=nEndNo; nRow++) 3727 { 3728 CRFlags nOld = rDoc.GetRowFlags(nRow,nTab); 3729 SCROW nLastRow = -1; 3730 bool bHidden = rDoc.RowHidden(nRow, nTab, nullptr, &nLastRow); 3731 if ( !bHidden && ( nOld & CRFlags::ManualSize ) ) 3732 rDoc.SetRowFlags( nRow, nTab, nOld & ~CRFlags::ManualSize ); 3733 } 3734 } 3735 3736 ScSizeDeviceProvider aProv( &rDocShell ); 3737 Fraction aOne(1,1); 3738 sc::RowHeightContext aCxt(rDoc.MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aOne, aOne, aProv.GetDevice()); 3739 aCxt.setForceAutoSize(bAll); 3740 rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, bApi); 3741 3742 if (bAll) 3743 rDoc.ShowRows( nStartNo, nEndNo, nTab, true ); 3744 3745 // Manual flag will be set already in SetOptimalHeight if bAll=true 3746 // (it is on when Extra-Height, otherwise off). 3747 } 3748 else if ( eMode==SC_SIZE_DIRECT || eMode==SC_SIZE_ORIGINAL ) 3749 { 3750 if (nSizeTwips) 3751 { 3752 rDoc.SetRowHeightRange( nStartNo, nEndNo, nTab, nSizeTwips ); 3753 rDoc.SetManualHeight( nStartNo, nEndNo, nTab, true ); // height was set manually 3754 } 3755 if ( eMode != SC_SIZE_ORIGINAL ) 3756 rDoc.ShowRows( nStartNo, nEndNo, nTab, nSizeTwips != 0 ); 3757 } 3758 else if ( eMode==SC_SIZE_SHOW ) 3759 { 3760 rDoc.ShowRows( nStartNo, nEndNo, nTab, true ); 3761 } 3762 } 3763 else // Column widths 3764 { 3765 for (SCCOL nCol=static_cast<SCCOL>(nStartNo); nCol<=static_cast<SCCOL>(nEndNo); nCol++) 3766 { 3767 if ( eMode != SC_SIZE_VISOPT || !rDoc.ColHidden(nCol, nTab) ) 3768 { 3769 sal_uInt16 nThisSize = nSizeTwips; 3770 3771 if ( eMode==SC_SIZE_OPTIMAL || eMode==SC_SIZE_VISOPT ) 3772 nThisSize = nSizeTwips + 3773 lcl_GetOptimalColWidth( rDocShell, nCol, nTab ); 3774 if ( nThisSize ) 3775 rDoc.SetColWidth( nCol, nTab, nThisSize ); 3776 3777 if ( eMode != SC_SIZE_ORIGINAL ) 3778 rDoc.ShowCol( nCol, nTab, bShow ); 3779 } 3780 } 3781 } 3782 3783 // adjust outlines 3784 3785 if ( eMode != SC_SIZE_ORIGINAL ) 3786 { 3787 if (bWidth) 3788 bOutline = bOutline || rDoc.UpdateOutlineCol( 3789 static_cast<SCCOL>(nStartNo), 3790 static_cast<SCCOL>(nEndNo), nTab, bShow ); 3791 else 3792 bOutline = bOutline || rDoc.UpdateOutlineRow( 3793 static_cast<SCROW>(nStartNo), 3794 static_cast<SCROW>(nEndNo), nTab, bShow ); 3795 } 3796 } 3797 rDoc.SetDrawPageSize(nTab); 3798 3799 if (!bOutline) 3800 pUndoTab.reset(); 3801 3802 if (bRecord) 3803 { 3804 ScMarkData aMark(rDoc.GetSheetLimits()); 3805 aMark.SelectOneTable( nTab ); 3806 rDocShell.GetUndoManager()->AddUndoAction( 3807 std::make_unique<ScUndoWidthOrHeight>( 3808 &rDocShell, aMark, nStart, nTab, nEnd, nTab, std::move(pUndoDoc), 3809 std::move(aUndoRanges), std::move(pUndoTab), eMode, nSizeTwips, bWidth)); 3810 } 3811 3812 rDoc.UpdatePageBreaks( nTab ); 3813 3814 ScTabViewShell* pViewSh = rDocShell.GetBestViewShell(); 3815 if (pViewSh) 3816 pViewSh->OnLOKSetWidthOrHeight(nStart, bWidth); 3817 3818 rDocShell.PostPaint(0,0,nTab,rDoc.MaxCol(),rDoc.MaxRow(),nTab,PaintPartFlags::All); 3819 aModificator.SetDocumentModified(); 3820 3821 return false; 3822 } 3823 3824 bool ScDocFunc::InsertPageBreak( bool bColumn, const ScAddress& rPos, 3825 bool bRecord, bool bSetModified ) 3826 { 3827 ScDocShellModificator aModificator( rDocShell ); 3828 3829 ScDocument& rDoc = rDocShell.GetDocument(); 3830 if (bRecord && !rDoc.IsUndoEnabled()) 3831 bRecord = false; 3832 SCTAB nTab = rPos.Tab(); 3833 SfxBindings* pBindings = rDocShell.GetViewBindings(); 3834 3835 SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) : 3836 static_cast<SCCOLROW>(rPos.Row()); 3837 if (nPos == 0) 3838 return false; // first column / row 3839 3840 ScBreakType nBreak = bColumn ? 3841 rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab) : 3842 rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab); 3843 if (nBreak & ScBreakType::Manual) 3844 return true; 3845 3846 if (bRecord) 3847 rDocShell.GetUndoManager()->AddUndoAction( 3848 std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, true ) ); 3849 3850 if (bColumn) 3851 rDoc.SetColBreak(static_cast<SCCOL>(nPos), nTab, false, true); 3852 else 3853 rDoc.SetRowBreak(static_cast<SCROW>(nPos), nTab, false, true); 3854 3855 rDoc.InvalidatePageBreaks(nTab); 3856 rDoc.UpdatePageBreaks( nTab ); 3857 3858 rDoc.SetStreamValid(nTab, false); 3859 3860 if (bColumn) 3861 { 3862 rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid ); 3863 if (pBindings) 3864 { 3865 pBindings->Invalidate( FID_INS_COLBRK ); 3866 pBindings->Invalidate( FID_DEL_COLBRK ); 3867 } 3868 } 3869 else 3870 { 3871 rDocShell.PostPaint( 0, static_cast<SCROW>(nPos)-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid ); 3872 if (pBindings) 3873 { 3874 pBindings->Invalidate( FID_INS_ROWBRK ); 3875 pBindings->Invalidate( FID_DEL_ROWBRK ); 3876 } 3877 } 3878 if (pBindings) 3879 pBindings->Invalidate( FID_DEL_MANUALBREAKS ); 3880 3881 if (bSetModified) 3882 aModificator.SetDocumentModified(); 3883 3884 return true; 3885 } 3886 3887 bool ScDocFunc::RemovePageBreak( bool bColumn, const ScAddress& rPos, 3888 bool bRecord, bool bSetModified ) 3889 { 3890 ScDocShellModificator aModificator( rDocShell ); 3891 3892 ScDocument& rDoc = rDocShell.GetDocument(); 3893 if (bRecord && !rDoc.IsUndoEnabled()) 3894 bRecord = false; 3895 SCTAB nTab = rPos.Tab(); 3896 SfxBindings* pBindings = rDocShell.GetViewBindings(); 3897 3898 SCCOLROW nPos = bColumn ? static_cast<SCCOLROW>(rPos.Col()) : 3899 static_cast<SCCOLROW>(rPos.Row()); 3900 3901 ScBreakType nBreak; 3902 if (bColumn) 3903 nBreak = rDoc.HasColBreak(static_cast<SCCOL>(nPos), nTab); 3904 else 3905 nBreak = rDoc.HasRowBreak(static_cast<SCROW>(nPos), nTab); 3906 if (!(nBreak & ScBreakType::Manual)) 3907 // There is no manual break. 3908 return false; 3909 3910 if (bRecord) 3911 rDocShell.GetUndoManager()->AddUndoAction( 3912 std::make_unique<ScUndoPageBreak>( &rDocShell, rPos.Col(), rPos.Row(), nTab, bColumn, false ) ); 3913 3914 if (bColumn) 3915 rDoc.RemoveColBreak(static_cast<SCCOL>(nPos), nTab, false, true); 3916 else 3917 rDoc.RemoveRowBreak(static_cast<SCROW>(nPos), nTab, false, true); 3918 3919 rDoc.UpdatePageBreaks( nTab ); 3920 3921 rDoc.SetStreamValid(nTab, false); 3922 3923 if (bColumn) 3924 { 3925 rDocShell.PostPaint( static_cast<SCCOL>(nPos)-1, 0, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid ); 3926 if (pBindings) 3927 { 3928 pBindings->Invalidate( FID_INS_COLBRK ); 3929 pBindings->Invalidate( FID_DEL_COLBRK ); 3930 } 3931 } 3932 else 3933 { 3934 rDocShell.PostPaint( 0, nPos-1, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid ); 3935 if (pBindings) 3936 { 3937 pBindings->Invalidate( FID_INS_ROWBRK ); 3938 pBindings->Invalidate( FID_DEL_ROWBRK ); 3939 } 3940 } 3941 if (pBindings) 3942 pBindings->Invalidate( FID_DEL_MANUALBREAKS ); 3943 3944 if (bSetModified) 3945 aModificator.SetDocumentModified(); 3946 3947 return true; 3948 } 3949 3950 void ScDocFunc::ProtectSheet( SCTAB nTab, const ScTableProtection& rProtect ) 3951 { 3952 ScDocument& rDoc = rDocShell.GetDocument(); 3953 3954 std::unique_ptr<ScTableProtection> p; 3955 if (!rProtect.isProtected() && rDoc.IsUndoEnabled()) 3956 { 3957 // In case of unprotecting, use a copy of passed ScTableProtection object for undo 3958 p = std::make_unique<ScTableProtection>(rProtect); 3959 } 3960 rDoc.SetTabProtection(nTab, &rProtect); 3961 if (rDoc.IsUndoEnabled()) 3962 { 3963 if (!p) 3964 { 3965 // For protection case, use a copy of resulting ScTableProtection for undo 3966 const ScTableProtection* pProtect = rDoc.GetTabProtection(nTab); 3967 p = std::make_unique<ScTableProtection>(*pProtect); 3968 } 3969 rDocShell.GetUndoManager()->AddUndoAction( 3970 std::make_unique<ScUndoTabProtect>(&rDocShell, nTab, std::move(p))); 3971 // ownership of unique_ptr now transferred to ScUndoTabProtect. 3972 } 3973 for (SfxViewFrame* fr = SfxViewFrame::GetFirst(&rDocShell); fr; 3974 fr = SfxViewFrame::GetNext(*fr, &rDocShell)) 3975 if (ScTabViewShell* pTabViewShell = dynamic_cast<ScTabViewShell*>(fr->GetViewShell())) 3976 pTabViewShell->SetTabProtectionSymbol(nTab, rProtect.isProtected()); 3977 rDocShell.PostPaintGridAll(); 3978 ScDocShellModificator aModificator(rDocShell); 3979 aModificator.SetDocumentModified(); 3980 } 3981 3982 void ScDocFunc::ProtectDocument(const ScDocProtection& rProtect) 3983 { 3984 ScDocument& rDoc = rDocShell.GetDocument(); 3985 3986 std::unique_ptr<ScDocProtection> p; 3987 if (!rProtect.isProtected() && rDoc.IsUndoEnabled()) 3988 { 3989 // In case of unprotecting, use a copy of passed ScTableProtection object for undo 3990 p = std::make_unique<ScDocProtection>(rProtect); 3991 } 3992 rDoc.SetDocProtection(&rProtect); 3993 if (rDoc.IsUndoEnabled()) 3994 { 3995 if (!p) 3996 { 3997 // For protection case, use a copy of resulting ScTableProtection for undo 3998 ScDocProtection* pProtect = rDoc.GetDocProtection(); 3999 p = std::make_unique<ScDocProtection>(*pProtect); 4000 } 4001 rDocShell.GetUndoManager()->AddUndoAction( 4002 std::make_unique<ScUndoDocProtect>(&rDocShell, std::move(p))); 4003 // ownership of unique_ptr now transferred to ScUndoTabProtect. 4004 } 4005 4006 rDocShell.PostPaintGridAll(); 4007 ScDocShellModificator aModificator(rDocShell); 4008 aModificator.SetDocumentModified(); 4009 } 4010 4011 bool ScDocFunc::Protect( SCTAB nTab, const OUString& rPassword ) 4012 { 4013 if (nTab == TABLEID_DOC) 4014 { 4015 // document protection 4016 ScDocProtection aProtection; 4017 aProtection.setProtected(true); 4018 aProtection.setPassword(rPassword); 4019 ProtectDocument(aProtection); 4020 4021 } 4022 else 4023 { 4024 // sheet protection 4025 4026 const ScTableProtection* pOldProtection = rDocShell.GetDocument().GetTabProtection(nTab); 4027 ::std::unique_ptr<ScTableProtection> pNewProtection(pOldProtection ? new ScTableProtection(*pOldProtection) : new ScTableProtection()); 4028 pNewProtection->setProtected(true); 4029 pNewProtection->setPassword(rPassword); 4030 ProtectSheet(nTab, *pNewProtection); 4031 } 4032 return true; 4033 } 4034 4035 bool ScDocFunc::Unprotect( SCTAB nTab, const OUString& rPassword, bool bApi ) 4036 { 4037 ScDocument& rDoc = rDocShell.GetDocument(); 4038 4039 if (nTab == TABLEID_DOC) 4040 { 4041 // document protection 4042 4043 ScDocProtection* pDocProtect = rDoc.GetDocProtection(); 4044 if (!pDocProtect || !pDocProtect->isProtected()) 4045 // already unprotected (should not happen)! 4046 return true; 4047 4048 if (!pDocProtect->verifyPassword(rPassword)) 4049 { 4050 if (!bApi) 4051 { 4052 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), 4053 VclMessageType::Info, VclButtonsType::Ok, 4054 ScResId(SCSTR_WRONGPASSWORD))); 4055 xInfoBox->run(); 4056 } 4057 return false; 4058 } 4059 4060 ScDocProtection aNewProtection(*pDocProtect); 4061 aNewProtection.setProtected(false); 4062 ProtectDocument(aNewProtection); 4063 4064 } 4065 else 4066 { 4067 // sheet protection 4068 4069 const ScTableProtection* pTabProtect = rDoc.GetTabProtection(nTab); 4070 if (!pTabProtect || !pTabProtect->isProtected()) 4071 // already unprotected (should not happen)! 4072 return true; 4073 if (!pTabProtect->verifyPassword(rPassword)) 4074 { 4075 if (!bApi) 4076 { 4077 std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), 4078 VclMessageType::Info, VclButtonsType::Ok, 4079 ScResId(SCSTR_WRONGPASSWORD))); 4080 xInfoBox->run(); 4081 } 4082 return false; 4083 } 4084 4085 ScTableProtection aNewProtection(*pTabProtect); 4086 aNewProtection.setProtected(false); 4087 ProtectSheet(nTab, aNewProtection); 4088 } 4089 4090 return true; 4091 } 4092 4093 void ScDocFunc::ClearItems( const ScMarkData& rMark, const sal_uInt16* pWhich, bool bApi ) 4094 { 4095 ScDocShellModificator aModificator( rDocShell ); 4096 4097 ScDocument& rDoc = rDocShell.GetDocument(); 4098 bool bUndo (rDoc.IsUndoEnabled()); 4099 ScEditableTester aTester( rDoc, rMark ); 4100 if (!aTester.IsEditable()) 4101 { 4102 if (!bApi) 4103 rDocShell.ErrorMessage(aTester.GetMessageId()); 4104 return; 4105 } 4106 4107 // #i12940# ClearItems is called (from setPropertyToDefault) directly with uno object's cached 4108 // MarkData (GetMarkData), so rMark must be changed to multi selection for ClearSelectionItems 4109 // here. 4110 4111 ScMarkData aMultiMark = rMark; 4112 aMultiMark.SetMarking(false); // for MarkToMulti 4113 aMultiMark.MarkToMulti(); 4114 const ScRange& aMarkRange = aMultiMark.GetMultiMarkArea(); 4115 4116 if (bUndo) 4117 { 4118 SCTAB nStartTab = aMarkRange.aStart.Tab(); 4119 SCTAB nEndTab = aMarkRange.aEnd.Tab(); 4120 4121 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 4122 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab ); 4123 rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &aMultiMark ); 4124 4125 rDocShell.GetUndoManager()->AddUndoAction( 4126 std::make_unique<ScUndoClearItems>( &rDocShell, aMultiMark, std::move(pUndoDoc), pWhich ) ); 4127 } 4128 4129 rDoc.ClearSelectionItems( pWhich, aMultiMark ); 4130 4131 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE ); 4132 aModificator.SetDocumentModified(); 4133 4134 //! Bindings-Invalidate etc.? 4135 } 4136 4137 bool ScDocFunc::ChangeIndent( const ScMarkData& rMark, bool bIncrement, bool bApi ) 4138 { 4139 ScDocShellModificator aModificator( rDocShell ); 4140 4141 ScDocument& rDoc = rDocShell.GetDocument(); 4142 bool bUndo(rDoc.IsUndoEnabled()); 4143 ScEditableTester aTester( rDoc, rMark ); 4144 if (!aTester.IsEditable()) 4145 { 4146 if (!bApi) 4147 rDocShell.ErrorMessage(aTester.GetMessageId()); 4148 return false; 4149 } 4150 4151 const ScRange& aMarkRange = rMark.GetMultiMarkArea(); 4152 4153 if (bUndo) 4154 { 4155 SCTAB nStartTab = aMarkRange.aStart.Tab(); 4156 SCTAB nTabCount = rDoc.GetTableCount(); 4157 4158 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 4159 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab ); 4160 for (const auto& rTab : rMark) 4161 { 4162 if (rTab >= nTabCount) 4163 break; 4164 4165 if (rTab != nStartTab) 4166 pUndoDoc->AddUndoTab( rTab, rTab ); 4167 } 4168 4169 ScRange aCopyRange = aMarkRange; 4170 aCopyRange.aStart.SetTab(0); 4171 aCopyRange.aEnd.SetTab(nTabCount-1); 4172 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, true, *pUndoDoc, &rMark ); 4173 4174 rDocShell.GetUndoManager()->AddUndoAction( 4175 std::make_unique<ScUndoIndent>( &rDocShell, rMark, std::move(pUndoDoc), bIncrement ) ); 4176 } 4177 4178 rDoc.ChangeSelectionIndent( bIncrement, rMark ); 4179 4180 rDocShell.PostPaint( aMarkRange, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE ); 4181 aModificator.SetDocumentModified(); 4182 4183 SfxBindings* pBindings = rDocShell.GetViewBindings(); 4184 if (pBindings) 4185 { 4186 pBindings->Invalidate( SID_ALIGNLEFT ); // ChangeIndent aligns left 4187 pBindings->Invalidate( SID_ALIGNRIGHT ); 4188 pBindings->Invalidate( SID_ALIGNBLOCK ); 4189 pBindings->Invalidate( SID_ALIGNCENTERHOR ); 4190 pBindings->Invalidate( SID_ATTR_LRSPACE ); 4191 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_LEFT ); 4192 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_RIGHT ); 4193 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_BLOCK ); 4194 pBindings->Invalidate( SID_ATTR_PARA_ADJUST_CENTER); 4195 // pseudo slots for Format menu 4196 pBindings->Invalidate( SID_ALIGN_ANY_HDEFAULT ); 4197 pBindings->Invalidate( SID_ALIGN_ANY_LEFT ); 4198 pBindings->Invalidate( SID_ALIGN_ANY_HCENTER ); 4199 pBindings->Invalidate( SID_ALIGN_ANY_RIGHT ); 4200 pBindings->Invalidate( SID_ALIGN_ANY_JUSTIFIED ); 4201 } 4202 4203 return true; 4204 } 4205 4206 bool ScDocFunc::AutoFormat( const ScRange& rRange, const ScMarkData* pTabMark, 4207 sal_uInt16 nFormatNo, bool bApi ) 4208 { 4209 ScDocShellModificator aModificator( rDocShell ); 4210 4211 ScDocument& rDoc = rDocShell.GetDocument(); 4212 SCCOL nStartCol = rRange.aStart.Col(); 4213 SCROW nStartRow = rRange.aStart.Row(); 4214 SCTAB nStartTab = rRange.aStart.Tab(); 4215 SCCOL nEndCol = rRange.aEnd.Col(); 4216 SCROW nEndRow = rRange.aEnd.Row(); 4217 SCTAB nEndTab = rRange.aEnd.Tab(); 4218 4219 bool bRecord = true; 4220 if (!rDoc.IsUndoEnabled()) 4221 bRecord = false; 4222 ScMarkData aMark(rDoc.GetSheetLimits()); 4223 if (pTabMark) 4224 aMark = *pTabMark; 4225 else 4226 { 4227 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4228 aMark.SelectTable( nTab, true ); 4229 } 4230 4231 ScAutoFormat* pAutoFormat = ScGlobal::GetOrCreateAutoFormat(); 4232 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark ); 4233 if ( nFormatNo < pAutoFormat->size() && aTester.IsEditable() ) 4234 { 4235 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4236 4237 bool bSize = pAutoFormat->findByIndex(nFormatNo)->GetIncludeWidthHeight(); 4238 4239 SCTAB nTabCount = rDoc.GetTableCount(); 4240 ScDocumentUniquePtr pUndoDoc; 4241 if ( bRecord ) 4242 { 4243 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4244 pUndoDoc->InitUndo( rDoc, nStartTab, nStartTab, bSize, bSize ); 4245 for (const auto& rTab : aMark) 4246 { 4247 if (rTab >= nTabCount) 4248 break; 4249 4250 if (rTab != nStartTab) 4251 pUndoDoc->AddUndoTab( rTab, rTab, bSize, bSize ); 4252 } 4253 4254 ScRange aCopyRange = rRange; 4255 aCopyRange.aStart.SetTab(0); 4256 aCopyRange.aStart.SetTab(nTabCount-1); 4257 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::ATTRIB, false, *pUndoDoc, &aMark ); 4258 if (bSize) 4259 { 4260 rDoc.CopyToDocument( nStartCol,0,0, nEndCol,rDoc.MaxRow(),nTabCount-1, 4261 InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark ); 4262 rDoc.CopyToDocument( 0,nStartRow,0, rDoc.MaxCol(),nEndRow,nTabCount-1, 4263 InsertDeleteFlags::NONE, false, *pUndoDoc, &aMark ); 4264 } 4265 rDoc.BeginDrawUndo(); 4266 } 4267 4268 rDoc.AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo, aMark ); 4269 4270 if (bSize) 4271 { 4272 std::vector<sc::ColRowSpan> aCols(1, sc::ColRowSpan(nStartCol,nEndCol)); 4273 std::vector<sc::ColRowSpan> aRows(1, sc::ColRowSpan(nStartRow,nEndRow)); 4274 4275 for (const auto& rTab : aMark) 4276 { 4277 if (rTab >= nTabCount) 4278 break; 4279 4280 SetWidthOrHeight(true, aCols, rTab, SC_SIZE_VISOPT, STD_EXTRA_WIDTH, false, true); 4281 SetWidthOrHeight(false, aRows, rTab, SC_SIZE_VISOPT, 0, false, false); 4282 rDocShell.PostPaint( 0,0,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab, 4283 PaintPartFlags::Grid | PaintPartFlags::Left | PaintPartFlags::Top ); 4284 } 4285 } 4286 else 4287 { 4288 for (const auto& rTab : aMark) 4289 { 4290 if (rTab >= nTabCount) 4291 break; 4292 4293 bool bAdj = AdjustRowHeight( ScRange(nStartCol, nStartRow, rTab, 4294 nEndCol, nEndRow, rTab), false, bApi ); 4295 if (bAdj) 4296 rDocShell.PostPaint( 0,nStartRow,rTab, rDoc.MaxCol(),rDoc.MaxRow(),rTab, 4297 PaintPartFlags::Grid | PaintPartFlags::Left ); 4298 else 4299 rDocShell.PostPaint( nStartCol, nStartRow, rTab, 4300 nEndCol, nEndRow, rTab, PaintPartFlags::Grid ); 4301 } 4302 } 4303 4304 if ( bRecord ) // only now is Draw-Undo available 4305 { 4306 rDocShell.GetUndoManager()->AddUndoAction( 4307 std::make_unique<ScUndoAutoFormat>( &rDocShell, rRange, std::move(pUndoDoc), aMark, bSize, nFormatNo ) ); 4308 } 4309 4310 aModificator.SetDocumentModified(); 4311 } 4312 else if (!bApi) 4313 rDocShell.ErrorMessage(aTester.GetMessageId()); 4314 4315 return false; 4316 } 4317 4318 bool ScDocFunc::EnterMatrix( const ScRange& rRange, const ScMarkData* pTabMark, 4319 const ScTokenArray* pTokenArray, const OUString& rString, bool bApi, bool bEnglish, 4320 const OUString& rFormulaNmsp, const formula::FormulaGrammar::Grammar eGrammar ) 4321 { 4322 if (ScViewData::SelectionFillDOOM( rRange )) 4323 return false; 4324 4325 ScDocShellModificator aModificator( rDocShell ); 4326 4327 bool bSuccess = false; 4328 ScDocument& rDoc = rDocShell.GetDocument(); 4329 SCCOL nStartCol = rRange.aStart.Col(); 4330 SCROW nStartRow = rRange.aStart.Row(); 4331 SCTAB nStartTab = rRange.aStart.Tab(); 4332 SCCOL nEndCol = rRange.aEnd.Col(); 4333 SCROW nEndRow = rRange.aEnd.Row(); 4334 SCTAB nEndTab = rRange.aEnd.Tab(); 4335 4336 ScMarkData aMark(rDoc.GetSheetLimits()); 4337 if (pTabMark) 4338 aMark = *pTabMark; 4339 else 4340 { 4341 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4342 aMark.SelectTable( nTab, true ); 4343 } 4344 4345 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark ); 4346 if ( aTester.IsEditable() ) 4347 { 4348 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4349 4350 ScDocumentUniquePtr pUndoDoc; 4351 4352 const bool bUndo(rDoc.IsUndoEnabled()); 4353 if (bUndo) 4354 { 4355 //! take selected sheets into account also when undoing 4356 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4357 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab ); 4358 rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc ); 4359 } 4360 4361 // use TokenArray if given, string (and flags) otherwise 4362 if ( pTokenArray ) 4363 { 4364 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow, 4365 aMark, OUString(), pTokenArray, eGrammar); 4366 } 4367 else if ( rDoc.IsImportingXML() ) 4368 { 4369 ScTokenArray aCode(rDoc); 4370 aCode.AssignXMLString( rString, 4371 ((eGrammar == formula::FormulaGrammar::GRAM_EXTERNAL) ? rFormulaNmsp : OUString())); 4372 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow, 4373 aMark, OUString(), &aCode, eGrammar); 4374 rDoc.IncXMLImportedFormulaCount( rString.getLength() ); 4375 } 4376 else if (bEnglish) 4377 { 4378 ScCompiler aComp( rDoc, rRange.aStart, eGrammar); 4379 std::unique_ptr<ScTokenArray> pCode = aComp.CompileString( rString ); 4380 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow, 4381 aMark, OUString(), pCode.get(), eGrammar); 4382 } 4383 else 4384 rDoc.InsertMatrixFormula( nStartCol, nStartRow, nEndCol, nEndRow, 4385 aMark, rString, nullptr, eGrammar); 4386 4387 if (bUndo) 4388 { 4389 //! take selected sheets into account also when undoing 4390 rDocShell.GetUndoManager()->AddUndoAction( 4391 std::make_unique<ScUndoEnterMatrix>( &rDocShell, rRange, std::move(pUndoDoc), rString ) ); 4392 } 4393 4394 // Err522 painting of DDE-Formulas will be intercepted during interpreting 4395 rDocShell.PostPaint( nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab, PaintPartFlags::Grid ); 4396 aModificator.SetDocumentModified(); 4397 4398 bSuccess = true; 4399 } 4400 else if (!bApi) 4401 rDocShell.ErrorMessage(aTester.GetMessageId()); 4402 4403 return bSuccess; 4404 } 4405 4406 bool ScDocFunc::TabOp( const ScRange& rRange, const ScMarkData* pTabMark, 4407 const ScTabOpParam& rParam, bool bRecord, bool bApi ) 4408 { 4409 ScDocShellModificator aModificator( rDocShell ); 4410 4411 bool bSuccess = false; 4412 ScDocument& rDoc = rDocShell.GetDocument(); 4413 SCCOL nStartCol = rRange.aStart.Col(); 4414 SCROW nStartRow = rRange.aStart.Row(); 4415 SCTAB nStartTab = rRange.aStart.Tab(); 4416 SCCOL nEndCol = rRange.aEnd.Col(); 4417 SCROW nEndRow = rRange.aEnd.Row(); 4418 SCTAB nEndTab = rRange.aEnd.Tab(); 4419 4420 if (bRecord && !rDoc.IsUndoEnabled()) 4421 bRecord = false; 4422 4423 ScMarkData aMark(rDoc.GetSheetLimits()); 4424 if (pTabMark) 4425 aMark = *pTabMark; 4426 else 4427 { 4428 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4429 aMark.SelectTable( nTab, true ); 4430 } 4431 4432 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark ); 4433 if ( aTester.IsEditable() ) 4434 { 4435 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4436 rDoc.SetDirty( rRange, false ); 4437 if ( bRecord ) 4438 { 4439 //! take selected sheets into account also when undoing 4440 ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); 4441 pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab ); 4442 rDoc.CopyToDocument( rRange, InsertDeleteFlags::ALL & ~InsertDeleteFlags::NOTE, false, *pUndoDoc ); 4443 4444 rDocShell.GetUndoManager()->AddUndoAction( 4445 std::make_unique<ScUndoTabOp>( &rDocShell, 4446 nStartCol, nStartRow, nStartTab, 4447 nEndCol, nEndRow, nEndTab, std::move(pUndoDoc), 4448 rParam.aRefFormulaCell, 4449 rParam.aRefFormulaEnd, 4450 rParam.aRefRowCell, 4451 rParam.aRefColCell, 4452 rParam.meMode) ); 4453 } 4454 rDoc.InsertTableOp(rParam, nStartCol, nStartRow, nEndCol, nEndRow, aMark); 4455 rDocShell.PostPaintGridAll(); 4456 aModificator.SetDocumentModified(); 4457 bSuccess = true; 4458 } 4459 else if (!bApi) 4460 rDocShell.ErrorMessage(aTester.GetMessageId()); 4461 4462 return bSuccess; 4463 } 4464 4465 static ScDirection DirFromFillDir( FillDir eDir ) 4466 { 4467 if (eDir==FILL_TO_BOTTOM) 4468 return DIR_BOTTOM; 4469 else if (eDir==FILL_TO_RIGHT) 4470 return DIR_RIGHT; 4471 else if (eDir==FILL_TO_TOP) 4472 return DIR_TOP; 4473 else // if (eDir==FILL_TO_LEFT) 4474 return DIR_LEFT; 4475 } 4476 4477 namespace { 4478 4479 /** 4480 * Expand the fill range as necessary, to allow copying of adjacent cell(s) 4481 * even when those cells are not in the original range. 4482 */ 4483 void adjustFillRangeForAdjacentCopy(const ScDocument &rDoc, ScRange& rRange, FillDir eDir) 4484 { 4485 switch (eDir) 4486 { 4487 case FILL_TO_BOTTOM: 4488 { 4489 if (rRange.aStart.Row() == 0) 4490 return; 4491 4492 if (rRange.aStart.Row() != rRange.aEnd.Row()) 4493 return; 4494 4495 // Include the above row. 4496 ScAddress& s = rRange.aStart; 4497 s.SetRow(s.Row()-1); 4498 } 4499 break; 4500 case FILL_TO_TOP: 4501 { 4502 if (rRange.aStart.Row() == rDoc.MaxRow()) 4503 return; 4504 4505 if (rRange.aStart.Row() != rRange.aEnd.Row()) 4506 return; 4507 4508 // Include the row below. 4509 ScAddress& e = rRange.aEnd; 4510 e.SetRow(e.Row()+1); 4511 } 4512 break; 4513 case FILL_TO_LEFT: 4514 { 4515 if (rRange.aStart.Col() == rDoc.MaxCol()) 4516 return; 4517 4518 if (rRange.aStart.Col() != rRange.aEnd.Col()) 4519 return; 4520 4521 // Include the column to the right. 4522 ScAddress& e = rRange.aEnd; 4523 e.SetCol(e.Col()+1); 4524 } 4525 break; 4526 case FILL_TO_RIGHT: 4527 { 4528 if (rRange.aStart.Col() == 0) 4529 return; 4530 4531 if (rRange.aStart.Col() != rRange.aEnd.Col()) 4532 return; 4533 4534 // Include the column to the left. 4535 ScAddress& s = rRange.aStart; 4536 s.SetCol(s.Col()-1); 4537 } 4538 break; 4539 default: 4540 ; 4541 } 4542 } 4543 4544 } 4545 4546 bool ScDocFunc::FillSimple( const ScRange& rRange, const ScMarkData* pTabMark, 4547 FillDir eDir, bool bApi ) 4548 { 4549 ScDocShellModificator aModificator( rDocShell ); 4550 ScDocument& rDoc = rDocShell.GetDocument(); 4551 4552 bool bSuccess = false; 4553 ScRange aRange = rRange; 4554 adjustFillRangeForAdjacentCopy(rDoc, aRange, eDir); 4555 4556 SCCOL nStartCol = aRange.aStart.Col(); 4557 SCROW nStartRow = aRange.aStart.Row(); 4558 SCTAB nStartTab = aRange.aStart.Tab(); 4559 SCCOL nEndCol = aRange.aEnd.Col(); 4560 SCROW nEndRow = aRange.aEnd.Row(); 4561 SCTAB nEndTab = aRange.aEnd.Tab(); 4562 4563 bool bRecord = true; 4564 if (!rDoc.IsUndoEnabled()) 4565 bRecord = false; 4566 4567 ScMarkData aMark(rDoc.GetSheetLimits()); 4568 if (pTabMark) 4569 aMark = *pTabMark; 4570 else 4571 { 4572 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4573 aMark.SelectTable( nTab, true ); 4574 } 4575 4576 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark ); 4577 if ( aTester.IsEditable() ) 4578 { 4579 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4580 4581 ScRange aSourceArea = aRange; 4582 ScRange aDestArea = aRange; 4583 4584 SCCOLROW nCount = 0; 4585 switch (eDir) 4586 { 4587 case FILL_TO_BOTTOM: 4588 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row(); 4589 aSourceArea.aEnd.SetRow( aSourceArea.aStart.Row() ); 4590 break; 4591 case FILL_TO_RIGHT: 4592 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col(); 4593 aSourceArea.aEnd.SetCol( aSourceArea.aStart.Col() ); 4594 break; 4595 case FILL_TO_TOP: 4596 nCount = aSourceArea.aEnd.Row()-aSourceArea.aStart.Row(); 4597 aSourceArea.aStart.SetRow( aSourceArea.aEnd.Row() ); 4598 break; 4599 case FILL_TO_LEFT: 4600 nCount = aSourceArea.aEnd.Col()-aSourceArea.aStart.Col(); 4601 aSourceArea.aStart.SetCol( aSourceArea.aEnd.Col() ); 4602 break; 4603 } 4604 4605 ScDocumentUniquePtr pUndoDoc; 4606 if ( bRecord ) 4607 { 4608 SCTAB nTabCount = rDoc.GetTableCount(); 4609 SCTAB nDestStartTab = aDestArea.aStart.Tab(); 4610 4611 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4612 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab ); 4613 for (const auto& rTab : aMark) 4614 { 4615 if (rTab >= nTabCount) 4616 break; 4617 4618 if (rTab != nDestStartTab) 4619 pUndoDoc->AddUndoTab( rTab, rTab ); 4620 } 4621 4622 ScRange aCopyRange = aDestArea; 4623 aCopyRange.aStart.SetTab(0); 4624 aCopyRange.aEnd.SetTab(nTabCount-1); 4625 rDoc.CopyToDocument( aCopyRange, InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark ); 4626 } 4627 4628 sal_uLong nProgCount; 4629 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP) 4630 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1; 4631 else 4632 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1; 4633 nProgCount *= nCount; 4634 ScProgress aProgress( rDoc.GetDocumentShell(), 4635 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true ); 4636 4637 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), 4638 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress, 4639 aMark, nCount, eDir, FILL_SIMPLE ); 4640 AdjustRowHeight(aRange, true, bApi); 4641 4642 if ( bRecord ) // only now is Draw-Undo available 4643 { 4644 rDocShell.GetUndoManager()->AddUndoAction( 4645 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark, 4646 eDir, FILL_SIMPLE, FILL_DAY, MAXDOUBLE, 1.0, 1e307) ); 4647 } 4648 4649 rDocShell.PostPaintGridAll(); 4650 aModificator.SetDocumentModified(); 4651 4652 bSuccess = true; 4653 } 4654 else if (!bApi) 4655 rDocShell.ErrorMessage(aTester.GetMessageId()); 4656 4657 return bSuccess; 4658 } 4659 4660 bool ScDocFunc::FillSeries( const ScRange& rRange, const ScMarkData* pTabMark, 4661 FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, 4662 double fStart, double fStep, double fMax, 4663 bool bApi ) 4664 { 4665 ScDocShellModificator aModificator( rDocShell ); 4666 4667 bool bSuccess = false; 4668 ScDocument& rDoc = rDocShell.GetDocument(); 4669 SCCOL nStartCol = rRange.aStart.Col(); 4670 SCROW nStartRow = rRange.aStart.Row(); 4671 SCTAB nStartTab = rRange.aStart.Tab(); 4672 SCCOL nEndCol = rRange.aEnd.Col(); 4673 SCROW nEndRow = rRange.aEnd.Row(); 4674 SCTAB nEndTab = rRange.aEnd.Tab(); 4675 4676 bool bRecord = true; 4677 if (!rDoc.IsUndoEnabled()) 4678 bRecord = false; 4679 4680 ScMarkData aMark(rDoc.GetSheetLimits()); 4681 if (pTabMark) 4682 aMark = *pTabMark; 4683 else 4684 { 4685 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4686 aMark.SelectTable( nTab, true ); 4687 } 4688 4689 ScEditableTester aTester( rDoc, nStartCol,nStartRow, nEndCol,nEndRow, aMark ); 4690 if ( aTester.IsEditable() ) 4691 { 4692 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4693 4694 ScRange aSourceArea = rRange; 4695 ScRange aDestArea = rRange; 4696 4697 SCSIZE nCount = rDoc.GetEmptyLinesInBlock( 4698 aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), aSourceArea.aStart.Tab(), 4699 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), aSourceArea.aEnd.Tab(), 4700 DirFromFillDir(eDir) ); 4701 4702 // keep at least one row/column as source range 4703 SCSIZE nTotLines = ( eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP ) ? 4704 static_cast<SCSIZE>( aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1 ) : 4705 static_cast<SCSIZE>( aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1 ); 4706 if ( nCount >= nTotLines ) 4707 nCount = nTotLines - 1; 4708 4709 switch (eDir) 4710 { 4711 case FILL_TO_BOTTOM: 4712 aSourceArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() - nCount ) ); 4713 break; 4714 case FILL_TO_RIGHT: 4715 aSourceArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() - nCount ) ); 4716 break; 4717 case FILL_TO_TOP: 4718 aSourceArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() + nCount ) ); 4719 break; 4720 case FILL_TO_LEFT: 4721 aSourceArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() + nCount ) ); 4722 break; 4723 } 4724 4725 ScDocumentUniquePtr pUndoDoc; 4726 if ( bRecord ) 4727 { 4728 SCTAB nTabCount = rDoc.GetTableCount(); 4729 SCTAB nDestStartTab = aDestArea.aStart.Tab(); 4730 4731 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4732 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab ); 4733 for (const auto& rTab : aMark) 4734 { 4735 if (rTab >= nTabCount) 4736 break; 4737 4738 if (rTab != nDestStartTab) 4739 pUndoDoc->AddUndoTab( rTab, rTab ); 4740 } 4741 4742 rDoc.CopyToDocument( 4743 aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0, 4744 aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1, 4745 InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark ); 4746 } 4747 4748 if (aDestArea.aStart.Col() <= aDestArea.aEnd.Col() && 4749 aDestArea.aStart.Row() <= aDestArea.aEnd.Row()) 4750 { 4751 if ( fStart != MAXDOUBLE ) 4752 { 4753 SCCOL nValX = (eDir == FILL_TO_LEFT) ? aDestArea.aEnd.Col() : aDestArea.aStart.Col(); 4754 SCROW nValY = (eDir == FILL_TO_TOP ) ? aDestArea.aEnd.Row() : aDestArea.aStart.Row(); 4755 SCTAB nTab = aDestArea.aStart.Tab(); 4756 rDoc.SetValue( nValX, nValY, nTab, fStart ); 4757 } 4758 4759 sal_uLong nProgCount; 4760 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP) 4761 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1; 4762 else 4763 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1; 4764 nProgCount *= nCount; 4765 ScProgress aProgress( rDoc.GetDocumentShell(), 4766 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true ); 4767 4768 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), 4769 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress, 4770 aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax ); 4771 AdjustRowHeight(rRange, true, bApi); 4772 4773 rDocShell.PostPaintGridAll(); 4774 aModificator.SetDocumentModified(); 4775 } 4776 4777 if ( bRecord ) // only now is Draw-Undo available 4778 { 4779 rDocShell.GetUndoManager()->AddUndoAction( 4780 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark, 4781 eDir, eCmd, eDateCmd, fStart, fStep, fMax) ); 4782 } 4783 4784 bSuccess = true; 4785 } 4786 else if (!bApi) 4787 rDocShell.ErrorMessage(aTester.GetMessageId()); 4788 4789 return bSuccess; 4790 } 4791 4792 bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, 4793 FillDir eDir, sal_uLong nCount, bool bApi ) 4794 { 4795 return FillAuto( rRange, pTabMark, eDir, FILL_AUTO, FILL_DAY, nCount, 1.0/*fStep*/, MAXDOUBLE/*fMax*/, true/*bRecord*/, bApi ); 4796 } 4797 4798 bool ScDocFunc::FillAuto( ScRange& rRange, const ScMarkData* pTabMark, FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, sal_uLong nCount, double fStep, double fMax, bool bRecord, bool bApi ) 4799 { 4800 ScDocShellModificator aModificator( rDocShell ); 4801 4802 ScDocument& rDoc = rDocShell.GetDocument(); 4803 SCCOL nStartCol = rRange.aStart.Col(); 4804 SCROW nStartRow = rRange.aStart.Row(); 4805 SCTAB nStartTab = rRange.aStart.Tab(); 4806 SCCOL nEndCol = rRange.aEnd.Col(); 4807 SCROW nEndRow = rRange.aEnd.Row(); 4808 SCTAB nEndTab = rRange.aEnd.Tab(); 4809 4810 if (bRecord && !rDoc.IsUndoEnabled()) 4811 bRecord = false; 4812 4813 ScMarkData aMark(rDoc.GetSheetLimits()); 4814 if (pTabMark) 4815 aMark = *pTabMark; 4816 else 4817 { 4818 for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) 4819 aMark.SelectTable( nTab, true ); 4820 } 4821 4822 ScRange aSourceArea = rRange; 4823 ScRange aDestArea = rRange; 4824 4825 switch (eDir) 4826 { 4827 case FILL_TO_BOTTOM: 4828 aDestArea.aEnd.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aEnd.Row() + nCount ) ); 4829 break; 4830 case FILL_TO_TOP: 4831 if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Row() )) 4832 { 4833 OSL_FAIL("FillAuto: Row < 0"); 4834 nCount = aSourceArea.aStart.Row(); 4835 } 4836 aDestArea.aStart.SetRow( sal::static_int_cast<SCROW>( aSourceArea.aStart.Row() - nCount ) ); 4837 break; 4838 case FILL_TO_RIGHT: 4839 aDestArea.aEnd.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aEnd.Col() + nCount ) ); 4840 break; 4841 case FILL_TO_LEFT: 4842 if (nCount > sal::static_int_cast<sal_uLong>( aSourceArea.aStart.Col() )) 4843 { 4844 OSL_FAIL("FillAuto: Col < 0"); 4845 nCount = aSourceArea.aStart.Col(); 4846 } 4847 aDestArea.aStart.SetCol( sal::static_int_cast<SCCOL>( aSourceArea.aStart.Col() - nCount ) ); 4848 break; 4849 default: 4850 OSL_FAIL("Wrong direction with FillAuto"); 4851 break; 4852 } 4853 4854 // Test for cell protection 4855 //! Source range can be protected !!! 4856 //! but can't contain matrix fragments !!! 4857 4858 ScEditableTester aTester( rDoc, aDestArea ); 4859 if ( !aTester.IsEditable() ) 4860 { 4861 if (!bApi) 4862 rDocShell.ErrorMessage(aTester.GetMessageId()); 4863 return false; 4864 } 4865 4866 if ( rDoc.HasSelectedBlockMatrixFragment( nStartCol, nStartRow, 4867 nEndCol, nEndRow, aMark ) ) 4868 { 4869 if (!bApi) 4870 rDocShell.ErrorMessage(STR_MATRIXFRAGMENTERR); 4871 return false; 4872 } 4873 4874 // FID_FILL_... slots should already had been disabled, check here for API 4875 // calls, no message. 4876 if (ScViewData::SelectionFillDOOM( aDestArea)) 4877 return false; 4878 4879 weld::WaitObject aWait( ScDocShell::GetActiveDialogParent() ); 4880 4881 ScDocumentUniquePtr pUndoDoc; 4882 if ( bRecord ) 4883 { 4884 SCTAB nTabCount = rDoc.GetTableCount(); 4885 SCTAB nDestStartTab = aDestArea.aStart.Tab(); 4886 4887 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4888 pUndoDoc->InitUndo( rDoc, nDestStartTab, nDestStartTab ); 4889 for (const auto& rTab : aMark) 4890 { 4891 if (rTab >= nTabCount) 4892 break; 4893 4894 if (rTab != nDestStartTab) 4895 pUndoDoc->AddUndoTab( rTab, rTab ); 4896 } 4897 4898 // do not clone note captions in undo document 4899 rDoc.CopyToDocument( 4900 aDestArea.aStart.Col(), aDestArea.aStart.Row(), 0, 4901 aDestArea.aEnd.Col(), aDestArea.aEnd.Row(), nTabCount-1, 4902 InsertDeleteFlags::AUTOFILL, false, *pUndoDoc, &aMark ); 4903 } 4904 4905 sal_uLong nProgCount; 4906 if (eDir == FILL_TO_BOTTOM || eDir == FILL_TO_TOP) 4907 nProgCount = aSourceArea.aEnd.Col() - aSourceArea.aStart.Col() + 1; 4908 else 4909 nProgCount = aSourceArea.aEnd.Row() - aSourceArea.aStart.Row() + 1; 4910 nProgCount *= nCount; 4911 ScProgress aProgress( rDoc.GetDocumentShell(), 4912 ScResId(STR_FILL_SERIES_PROGRESS), nProgCount, true ); 4913 4914 rDoc.Fill( aSourceArea.aStart.Col(), aSourceArea.aStart.Row(), 4915 aSourceArea.aEnd.Col(), aSourceArea.aEnd.Row(), &aProgress, 4916 aMark, nCount, eDir, eCmd, eDateCmd, fStep, fMax ); 4917 4918 AdjustRowHeight(aDestArea, true, bApi); 4919 4920 if ( bRecord ) // only now is Draw-Undo available 4921 { 4922 rDocShell.GetUndoManager()->AddUndoAction( 4923 std::make_unique<ScUndoAutoFill>( &rDocShell, aDestArea, aSourceArea, std::move(pUndoDoc), aMark, 4924 eDir, eCmd, eDateCmd, MAXDOUBLE, fStep, fMax) ); 4925 } 4926 4927 rDocShell.PostPaintGridAll(); 4928 aModificator.SetDocumentModified(); 4929 4930 rRange = aDestArea; // return destination range (for marking) 4931 return true; 4932 } 4933 4934 bool ScDocFunc::MergeCells( const ScCellMergeOption& rOption, bool bContents, bool bRecord, bool bApi, bool bEmptyMergedCells /*=false*/ ) 4935 { 4936 using ::std::set; 4937 4938 ScDocShellModificator aModificator( rDocShell ); 4939 4940 SCCOL nStartCol = rOption.mnStartCol; 4941 SCROW nStartRow = rOption.mnStartRow; 4942 SCCOL nEndCol = rOption.mnEndCol; 4943 SCROW nEndRow = rOption.mnEndRow; 4944 if ((nStartCol == nEndCol && nStartRow == nEndRow) || rOption.maTabs.empty()) 4945 { 4946 // Nothing to do. Bail out quickly 4947 return true; 4948 } 4949 4950 ScDocument& rDoc = rDocShell.GetDocument(); 4951 SCTAB nTab1 = *rOption.maTabs.begin(), nTab2 = *rOption.maTabs.rbegin(); 4952 4953 if (bRecord && !rDoc.IsUndoEnabled()) 4954 bRecord = false; 4955 4956 for (const auto& rTab : rOption.maTabs) 4957 { 4958 ScEditableTester aTester( rDoc, rTab, nStartCol, nStartRow, nEndCol, nEndRow ); 4959 if (!aTester.IsEditable()) 4960 { 4961 if (!bApi) 4962 rDocShell.ErrorMessage(aTester.GetMessageId()); 4963 return false; 4964 } 4965 4966 if ( rDoc.HasAttrib( nStartCol, nStartRow, rTab, nEndCol, nEndRow, rTab, 4967 HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) 4968 { 4969 // "Merge of already merged cells not possible" 4970 if (!bApi) 4971 rDocShell.ErrorMessage(STR_MSSG_MERGECELLS_0); 4972 return false; 4973 } 4974 } 4975 4976 ScDocumentUniquePtr pUndoDoc; 4977 bool bNeedContentsUndo = false; 4978 for (const SCTAB nTab : rOption.maTabs) 4979 { 4980 bool bIsBlockEmpty = ( nStartRow == nEndRow ) 4981 ? rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab ) 4982 : rDoc.IsEmptyData( nStartCol,nStartRow+1, nStartCol,nEndRow, nTab ) && 4983 rDoc.IsEmptyData( nStartCol+1,nStartRow, nEndCol,nEndRow, nTab ); 4984 bool bNeedContents = bContents && !bIsBlockEmpty; 4985 bool bNeedEmpty = bEmptyMergedCells && !bIsBlockEmpty && !bNeedContents; // if DoMergeContents then cells are emptied 4986 4987 if (bRecord) 4988 { 4989 // test if the range contains other notes which also implies that we need an undo document 4990 bool bHasNotes = rDoc.HasNote(nTab, nStartCol, nStartRow, nEndCol, nEndRow); 4991 if (!pUndoDoc) 4992 { 4993 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 4994 pUndoDoc->InitUndo(rDoc, nTab1, nTab2); 4995 } 4996 // note captions are collected by drawing undo 4997 rDoc.CopyToDocument( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab, 4998 InsertDeleteFlags::ALL|InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc ); 4999 if( bHasNotes ) 5000 rDoc.BeginDrawUndo(); 5001 } 5002 5003 if (bNeedContents) 5004 rDoc.DoMergeContents( nStartCol,nStartRow, nEndCol,nEndRow, nTab ); 5005 else if ( bNeedEmpty ) 5006 rDoc.DoEmptyBlock( nStartCol,nStartRow, nEndCol,nEndRow, nTab ); 5007 rDoc.DoMerge( nStartCol,nStartRow, nEndCol,nEndRow, nTab ); 5008 5009 if (rOption.mbCenter) 5010 { 5011 rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxHorJustifyItem( SvxCellHorJustify::Center, ATTR_HOR_JUSTIFY ) ); 5012 rDoc.ApplyAttr( nStartCol, nStartRow, nTab, SvxVerJustifyItem( SvxCellVerJustify::Center, ATTR_VER_JUSTIFY ) ); 5013 } 5014 5015 if ( !AdjustRowHeight( ScRange( 0,nStartRow,nTab, rDoc.MaxCol(),nEndRow,nTab ), true, bApi ) ) 5016 rDocShell.PostPaint( nStartCol, nStartRow, nTab, 5017 nEndCol, nEndRow, nTab, PaintPartFlags::Grid ); 5018 if (bNeedContents || rOption.mbCenter) 5019 { 5020 ScRange aRange(nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab); 5021 rDoc.SetDirty(aRange, true); 5022 } 5023 5024 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles ); 5025 if(bDone) 5026 DetectiveMarkInvalid(nTab); 5027 5028 bNeedContentsUndo |= bNeedContents; 5029 } 5030 5031 if (pUndoDoc) 5032 { 5033 std::unique_ptr<SdrUndoGroup> pDrawUndo = rDoc.GetDrawLayer() ? rDoc.GetDrawLayer()->GetCalcUndo() : nullptr; 5034 rDocShell.GetUndoManager()->AddUndoAction( 5035 std::make_unique<ScUndoMerge>(&rDocShell, rOption, bNeedContentsUndo, std::move(pUndoDoc), std::move(pDrawUndo)) ); 5036 } 5037 5038 aModificator.SetDocumentModified(); 5039 5040 SfxBindings* pBindings = rDocShell.GetViewBindings(); 5041 if (pBindings) 5042 { 5043 pBindings->Invalidate( FID_MERGE_ON ); 5044 pBindings->Invalidate( FID_MERGE_OFF ); 5045 pBindings->Invalidate( FID_MERGE_TOGGLE ); 5046 } 5047 5048 return true; 5049 } 5050 5051 bool ScDocFunc::UnmergeCells( const ScRange& rRange, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge ) 5052 { 5053 ScCellMergeOption aOption(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); 5054 SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab(); 5055 for (SCTAB i = nTab1; i <= nTab2; ++i) 5056 aOption.maTabs.insert(i); 5057 5058 return UnmergeCells(aOption, bRecord, pUndoRemoveMerge); 5059 } 5060 5061 bool ScDocFunc::UnmergeCells( const ScCellMergeOption& rOption, bool bRecord, ScUndoRemoveMerge* pUndoRemoveMerge ) 5062 { 5063 using ::std::set; 5064 5065 if (rOption.maTabs.empty()) 5066 // Nothing to unmerge. 5067 return true; 5068 5069 ScDocShellModificator aModificator( rDocShell ); 5070 ScDocument& rDoc = rDocShell.GetDocument(); 5071 5072 if (bRecord && !rDoc.IsUndoEnabled()) 5073 bRecord = false; 5074 5075 ScDocument* pUndoDoc = (pUndoRemoveMerge ? pUndoRemoveMerge->GetUndoDoc() : nullptr); 5076 assert( pUndoDoc || !pUndoRemoveMerge ); 5077 for (const SCTAB nTab : rOption.maTabs) 5078 { 5079 ScRange aRange = rOption.getSingleRange(nTab); 5080 if ( !rDoc.HasAttrib(aRange, HasAttrFlags::Merged) ) 5081 continue; 5082 5083 ScRange aExtended = aRange; 5084 rDoc.ExtendMerge(aExtended); 5085 ScRange aRefresh = aExtended; 5086 rDoc.ExtendOverlapped(aRefresh); 5087 5088 if (bRecord) 5089 { 5090 if (!pUndoDoc) 5091 { 5092 pUndoDoc = new ScDocument( SCDOCMODE_UNDO ); 5093 pUndoDoc->InitUndo(rDoc, *rOption.maTabs.begin(), *rOption.maTabs.rbegin()); 5094 } 5095 rDoc.CopyToDocument(aExtended, InsertDeleteFlags::ATTRIB, false, *pUndoDoc); 5096 } 5097 5098 const SfxPoolItem& rDefAttr = rDoc.GetPool()->GetDefaultItem( ATTR_MERGE ); 5099 ScPatternAttr aPattern( rDoc.GetPool() ); 5100 aPattern.GetItemSet().Put( rDefAttr ); 5101 rDoc.ApplyPatternAreaTab( aRange.aStart.Col(), aRange.aStart.Row(), 5102 aRange.aEnd.Col(), aRange.aEnd.Row(), nTab, 5103 aPattern ); 5104 5105 rDoc.RemoveFlagsTab( aExtended.aStart.Col(), aExtended.aStart.Row(), 5106 aExtended.aEnd.Col(), aExtended.aEnd.Row(), nTab, 5107 ScMF::Hor | ScMF::Ver ); 5108 5109 rDoc.ExtendMerge( aRefresh, true ); 5110 5111 if ( !AdjustRowHeight( aExtended, true, true ) ) 5112 rDocShell.PostPaint( aExtended, PaintPartFlags::Grid ); 5113 5114 bool bDone = ScDetectiveFunc(rDoc, nTab).DeleteAll( ScDetectiveDelete::Circles ); 5115 if(bDone) 5116 DetectiveMarkInvalid(nTab); 5117 } 5118 5119 if (bRecord) 5120 { 5121 if (pUndoRemoveMerge) 5122 { 5123 // If pUndoRemoveMerge was passed, the caller is responsible for 5124 // adding it to Undo. Just add the current option. 5125 pUndoRemoveMerge->AddCellMergeOption( rOption); 5126 } 5127 else 5128 { 5129 rDocShell.GetUndoManager()->AddUndoAction( 5130 std::make_unique<ScUndoRemoveMerge>( &rDocShell, rOption, ScDocumentUniquePtr(pUndoDoc) ) ); 5131 } 5132 } 5133 aModificator.SetDocumentModified(); 5134 5135 return true; 5136 } 5137 5138 void ScDocFunc::ModifyRangeNames( const ScRangeName& rNewRanges, SCTAB nTab ) 5139 { 5140 SetNewRangeNames( std::unique_ptr<ScRangeName>(new ScRangeName(rNewRanges)), true, nTab ); 5141 } 5142 5143 void ScDocFunc::SetNewRangeNames( std::unique_ptr<ScRangeName> pNewRanges, bool bModifyDoc, SCTAB nTab ) // takes ownership of pNewRanges 5144 { 5145 ScDocShellModificator aModificator( rDocShell ); 5146 5147 OSL_ENSURE( pNewRanges, "pNewRanges is 0" ); 5148 ScDocument& rDoc = rDocShell.GetDocument(); 5149 bool bUndo(rDoc.IsUndoEnabled()); 5150 5151 if (bUndo) 5152 { 5153 ScRangeName* pOld; 5154 if (nTab >=0) 5155 { 5156 pOld = rDoc.GetRangeName(nTab); 5157 } 5158 else 5159 { 5160 pOld = rDoc.GetRangeName(); 5161 } 5162 std::unique_ptr<ScRangeName> pUndoRanges(new ScRangeName(*pOld)); 5163 std::unique_ptr<ScRangeName> pRedoRanges(new ScRangeName(*pNewRanges)); 5164 rDocShell.GetUndoManager()->AddUndoAction( 5165 std::make_unique<ScUndoRangeNames>( &rDocShell, std::move(pUndoRanges), std::move(pRedoRanges), nTab ) ); 5166 } 5167 5168 // #i55926# While loading XML, formula cells only have a single string token, 5169 // so CompileNameFormula would never find any name (index) tokens, and would 5170 // unnecessarily loop through all cells. 5171 bool bCompile = ( !rDoc.IsImportingXML() && rDoc.GetNamedRangesLockCount() == 0 ); 5172 5173 if ( bCompile ) 5174 rDoc.PreprocessRangeNameUpdate(); 5175 if (nTab >= 0) 5176 rDoc.SetRangeName( nTab, std::move(pNewRanges) ); // takes ownership 5177 else 5178 rDoc.SetRangeName( std::move(pNewRanges) ); // takes ownership 5179 if ( bCompile ) 5180 rDoc.CompileHybridFormula(); 5181 5182 if (bModifyDoc) 5183 { 5184 aModificator.SetDocumentModified(); 5185 SfxGetpApp()->Broadcast( SfxHint(SfxHintId::ScAreasChanged) ); 5186 } 5187 } 5188 5189 void ScDocFunc::ModifyAllRangeNames(const std::map<OUString, ScRangeName>& rRangeMap) 5190 { 5191 ScDocShellModificator aModificator(rDocShell); 5192 ScDocument& rDoc = rDocShell.GetDocument(); 5193 5194 if (rDoc.IsUndoEnabled()) 5195 { 5196 std::map<OUString, ScRangeName*> aOldRangeMap; 5197 rDoc.GetRangeNameMap(aOldRangeMap); 5198 rDocShell.GetUndoManager()->AddUndoAction( 5199 std::make_unique<ScUndoAllRangeNames>(&rDocShell, aOldRangeMap, rRangeMap)); 5200 } 5201 5202 rDoc.PreprocessAllRangeNamesUpdate(rRangeMap); 5203 rDoc.SetAllRangeNames(rRangeMap); 5204 rDoc.CompileHybridFormula(); 5205 5206 aModificator.SetDocumentModified(); 5207 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged)); 5208 } 5209 5210 void ScDocFunc::CreateOneName( ScRangeName& rList, 5211 SCCOL nPosX, SCROW nPosY, SCTAB nTab, 5212 SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, 5213 bool& rCancel, bool bApi ) 5214 { 5215 if (rCancel) 5216 return; 5217 5218 ScDocument& rDoc = rDocShell.GetDocument(); 5219 if (rDoc.HasValueData( nPosX, nPosY, nTab )) 5220 return; 5221 5222 OUString aName = rDoc.GetString(nPosX, nPosY, nTab); 5223 ScRangeData::MakeValidName(rDoc, aName); 5224 if (aName.isEmpty()) 5225 return; 5226 5227 OUString aContent( ScRange( nX1, nY1, nTab, nX2, nY2, nTab ).Format( 5228 rDoc, ScRefFlags::RANGE_ABS_3D, ScAddress::Details( rDoc.GetAddressConvention(), nPosY, nPosX))); 5229 5230 bool bInsert = false; 5231 ScRangeData* pOld = rList.findByUpperName(ScGlobal::getCharClass().uppercase(aName)); 5232 if (pOld) 5233 { 5234 OUString aOldStr = pOld->GetSymbol(); 5235 if (aOldStr != aContent) 5236 { 5237 if (bApi) 5238 bInsert = true; // don't check via API 5239 else 5240 { 5241 OUString aTemplate = ScResId( STR_CREATENAME_REPLACE ); 5242 OUString aMessage = o3tl::getToken(aTemplate, 0, '#' ) + aName + o3tl::getToken(aTemplate, 1, '#' ); 5243 5244 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(ScDocShell::GetActiveDialogParent(), 5245 VclMessageType::Question, VclButtonsType::YesNo, 5246 aMessage)); 5247 xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); 5248 xQueryBox->set_default_response(RET_YES); 5249 5250 short nResult = xQueryBox->run(); 5251 if ( nResult == RET_YES ) 5252 { 5253 rList.erase(*pOld); 5254 bInsert = true; 5255 } 5256 else if ( nResult == RET_CANCEL ) 5257 rCancel = true; 5258 } 5259 } 5260 } 5261 else 5262 bInsert = true; 5263 5264 if (bInsert) 5265 { 5266 ScRangeData* pData = new ScRangeData( rDoc, aName, aContent, 5267 ScAddress( nPosX, nPosY, nTab)); 5268 if (!rList.insert(pData)) 5269 { 5270 OSL_FAIL("nanu?"); 5271 } 5272 } 5273 } 5274 5275 bool ScDocFunc::CreateNames( const ScRange& rRange, CreateNameFlags nFlags, bool bApi, SCTAB aTab ) 5276 { 5277 if (nFlags == CreateNameFlags::NONE) 5278 return false; // was nothing 5279 5280 ScDocShellModificator aModificator( rDocShell ); 5281 5282 bool bDone = false; 5283 SCCOL nStartCol = rRange.aStart.Col(); 5284 SCROW nStartRow = rRange.aStart.Row(); 5285 SCCOL nEndCol = rRange.aEnd.Col(); 5286 SCROW nEndRow = rRange.aEnd.Row(); 5287 SCTAB nTab = rRange.aStart.Tab(); 5288 OSL_ENSURE(rRange.aEnd.Tab() == nTab, "CreateNames: multiple tables not possible"); 5289 5290 bool bValid = true; 5291 if ( nFlags & ( CreateNameFlags::Top | CreateNameFlags::Bottom ) ) 5292 if ( nStartRow == nEndRow ) 5293 bValid = false; 5294 if ( nFlags & ( CreateNameFlags::Left | CreateNameFlags::Right ) ) 5295 if ( nStartCol == nEndCol ) 5296 bValid = false; 5297 5298 if (bValid) 5299 { 5300 ScDocument& rDoc = rDocShell.GetDocument(); 5301 ScRangeName* pNames; 5302 if (aTab >=0) 5303 pNames = rDoc.GetRangeName(nTab); 5304 else 5305 pNames = rDoc.GetRangeName(); 5306 5307 if (!pNames) 5308 return false; // shouldn't happen 5309 ScRangeName aNewRanges( *pNames ); 5310 5311 bool bTop ( nFlags & CreateNameFlags::Top ); 5312 bool bLeft ( nFlags & CreateNameFlags::Left ); 5313 bool bBottom( nFlags & CreateNameFlags::Bottom ); 5314 bool bRight ( nFlags & CreateNameFlags::Right ); 5315 5316 SCCOL nContX1 = nStartCol; 5317 SCROW nContY1 = nStartRow; 5318 SCCOL nContX2 = nEndCol; 5319 SCROW nContY2 = nEndRow; 5320 5321 if ( bTop ) 5322 ++nContY1; 5323 if ( bLeft ) 5324 ++nContX1; 5325 if ( bBottom ) 5326 --nContY2; 5327 if ( bRight ) 5328 --nContX2; 5329 5330 bool bCancel = false; 5331 SCCOL i; 5332 SCROW j; 5333 5334 if ( bTop ) 5335 for (i=nContX1; i<=nContX2; i++) 5336 CreateOneName( aNewRanges, i,nStartRow,nTab, i,nContY1,i,nContY2, bCancel, bApi ); 5337 if ( bLeft ) 5338 for (j=nContY1; j<=nContY2; j++) 5339 CreateOneName( aNewRanges, nStartCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi ); 5340 if ( bBottom ) 5341 for (i=nContX1; i<=nContX2; i++) 5342 CreateOneName( aNewRanges, i,nEndRow,nTab, i,nContY1,i,nContY2, bCancel, bApi ); 5343 if ( bRight ) 5344 for (j=nContY1; j<=nContY2; j++) 5345 CreateOneName( aNewRanges, nEndCol,j,nTab, nContX1,j,nContX2,j, bCancel, bApi ); 5346 5347 if ( bTop && bLeft ) 5348 CreateOneName( aNewRanges, nStartCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi ); 5349 if ( bTop && bRight ) 5350 CreateOneName( aNewRanges, nEndCol,nStartRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi ); 5351 if ( bBottom && bLeft ) 5352 CreateOneName( aNewRanges, nStartCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi ); 5353 if ( bBottom && bRight ) 5354 CreateOneName( aNewRanges, nEndCol,nEndRow,nTab, nContX1,nContY1,nContX2,nContY2, bCancel, bApi ); 5355 5356 ModifyRangeNames( aNewRanges, aTab ); 5357 bDone = true; 5358 5359 } 5360 5361 return bDone; 5362 } 5363 5364 bool ScDocFunc::InsertNameList( const ScAddress& rStartPos, bool bApi ) 5365 { 5366 ScDocShellModificator aModificator( rDocShell ); 5367 5368 bool bDone = false; 5369 ScDocument& rDoc = rDocShell.GetDocument(); 5370 const bool bRecord = rDoc.IsUndoEnabled(); 5371 SCTAB nTab = rStartPos.Tab(); 5372 5373 //local names have higher priority than global names 5374 ScRangeName* pLocalList = rDoc.GetRangeName(nTab); 5375 sal_uInt16 nValidCount = 0; 5376 for (const auto& rEntry : *pLocalList) 5377 { 5378 const ScRangeData& r = *rEntry.second; 5379 if (!r.HasType(ScRangeData::Type::Database)) 5380 ++nValidCount; 5381 } 5382 ScRangeName* pList = rDoc.GetRangeName(); 5383 for (const auto& rEntry : *pList) 5384 { 5385 const ScRangeData& r = *rEntry.second; 5386 if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(r.GetUpperName())) 5387 ++nValidCount; 5388 } 5389 5390 if (nValidCount) 5391 { 5392 SCCOL nStartCol = rStartPos.Col(); 5393 SCROW nStartRow = rStartPos.Row(); 5394 SCCOL nEndCol = nStartCol + 1; 5395 SCROW nEndRow = nStartRow + static_cast<SCROW>(nValidCount) - 1; 5396 5397 ScEditableTester aTester( rDoc, nTab, nStartCol,nStartRow, nEndCol,nEndRow ); 5398 if (aTester.IsEditable()) 5399 { 5400 ScDocumentUniquePtr pUndoDoc; 5401 5402 if (bRecord) 5403 { 5404 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 5405 pUndoDoc->InitUndo( rDoc, nTab, nTab ); 5406 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, 5407 InsertDeleteFlags::ALL, false, *pUndoDoc); 5408 5409 rDoc.BeginDrawUndo(); // because of adjusting heights 5410 } 5411 5412 std::unique_ptr<ScRangeData*[]> ppSortArray(new ScRangeData* [ nValidCount ]); 5413 sal_uInt16 j = 0; 5414 for (const auto& rEntry : *pLocalList) 5415 { 5416 ScRangeData& r = *rEntry.second; 5417 if (!r.HasType(ScRangeData::Type::Database)) 5418 ppSortArray[j++] = &r; 5419 } 5420 for (const auto& [rName, rxData] : *pList) 5421 { 5422 ScRangeData& r = *rxData; 5423 if (!r.HasType(ScRangeData::Type::Database) && !pLocalList->findByUpperName(rName)) 5424 ppSortArray[j++] = &r; 5425 } 5426 qsort( static_cast<void*>(ppSortArray.get()), nValidCount, sizeof(ScRangeData*), 5427 &ScRangeData_QsortNameCompare ); 5428 OUString aName; 5429 OUStringBuffer aContent; 5430 OUString aFormula; 5431 SCROW nOutRow = nStartRow; 5432 for (j=0; j<nValidCount; j++) 5433 { 5434 ScRangeData* pData = ppSortArray[j]; 5435 pData->GetName(aName); 5436 // adjust relative references to the left column in Excel-compliant way: 5437 pData->UpdateSymbol(aContent, ScAddress( nStartCol, nOutRow, nTab )); 5438 aFormula = "=" + aContent; 5439 ScSetStringParam aParam; 5440 aParam.setTextInput(); 5441 rDoc.SetString(ScAddress(nStartCol,nOutRow,nTab), aName, &aParam); 5442 rDoc.SetString(ScAddress(nEndCol,nOutRow,nTab), aFormula, &aParam); 5443 ++nOutRow; 5444 } 5445 5446 ppSortArray.reset(); 5447 5448 if (bRecord) 5449 { 5450 ScDocumentUniquePtr pRedoDoc(new ScDocument( SCDOCMODE_UNDO )); 5451 pRedoDoc->InitUndo( rDoc, nTab, nTab ); 5452 rDoc.CopyToDocument(nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, 5453 InsertDeleteFlags::ALL, false, *pRedoDoc); 5454 5455 rDocShell.GetUndoManager()->AddUndoAction( 5456 std::make_unique<ScUndoListNames>( &rDocShell, 5457 ScRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ), 5458 std::move(pUndoDoc), std::move(pRedoDoc) ) ); 5459 } 5460 5461 if (!AdjustRowHeight(ScRange(0,nStartRow,nTab,rDoc.MaxCol(),nEndRow,nTab), true, true)) 5462 rDocShell.PostPaint( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab, PaintPartFlags::Grid ); 5463 5464 aModificator.SetDocumentModified(); 5465 bDone = true; 5466 } 5467 else if (!bApi) 5468 rDocShell.ErrorMessage(aTester.GetMessageId()); 5469 } 5470 return bDone; 5471 } 5472 5473 void ScDocFunc::ResizeMatrix( const ScRange& rOldRange, const ScAddress& rNewEnd ) 5474 { 5475 ScDocument& rDoc = rDocShell.GetDocument(); 5476 SCCOL nStartCol = rOldRange.aStart.Col(); 5477 SCROW nStartRow = rOldRange.aStart.Row(); 5478 SCTAB nTab = rOldRange.aStart.Tab(); 5479 5480 OUString aFormula = rDoc.GetFormula( nStartCol, nStartRow, nTab ); 5481 if ( !(aFormula.startsWith("{") && aFormula.endsWith("}")) ) 5482 return; 5483 5484 OUString aUndo = ScResId( STR_UNDO_RESIZEMATRIX ); 5485 bool bUndo(rDoc.IsUndoEnabled()); 5486 if (bUndo) 5487 { 5488 ViewShellId nViewShellId(1); 5489 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) 5490 nViewShellId = pViewSh->GetViewShellId(); 5491 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId ); 5492 } 5493 5494 aFormula = aFormula.copy(1, aFormula.getLength()-2); 5495 5496 ScMarkData aMark(rDoc.GetSheetLimits()); 5497 aMark.SetMarkArea( rOldRange ); 5498 aMark.SelectTable( nTab, true ); 5499 ScRange aNewRange( rOldRange.aStart, rNewEnd ); 5500 5501 if ( DeleteContents( aMark, InsertDeleteFlags::CONTENTS, true, false/*bApi*/ ) ) 5502 { 5503 // GRAM_API for API compatibility. 5504 if (!EnterMatrix( aNewRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API )) 5505 { 5506 // try to restore the previous state 5507 EnterMatrix( rOldRange, &aMark, nullptr, aFormula, false/*bApi*/, false, OUString(), formula::FormulaGrammar::GRAM_API ); 5508 } 5509 } 5510 5511 if (bUndo) 5512 rDocShell.GetUndoManager()->LeaveListAction(); 5513 } 5514 5515 void ScDocFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter, 5516 const OUString& rOptions, const OUString& rSource, 5517 const ScRange& rDestRange, sal_Int32 nRefreshDelaySeconds, 5518 bool bFitBlock, bool bApi ) 5519 { 5520 ScDocument& rDoc = rDocShell.GetDocument(); 5521 bool bUndo (rDoc.IsUndoEnabled()); 5522 5523 sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager(); 5524 5525 // #i52120# if other area links exist at the same start position, 5526 // remove them first (file format specifies only one link definition 5527 // for a cell) 5528 5529 sal_uInt16 nLinkCount = pLinkManager->GetLinks().size(); 5530 sal_uInt16 nRemoved = 0; 5531 sal_uInt16 nLinkPos = 0; 5532 while (nLinkPos<nLinkCount) 5533 { 5534 ::sfx2::SvBaseLink* pBase = pLinkManager->GetLinks()[nLinkPos].get(); 5535 ScAreaLink* pLink = dynamic_cast<ScAreaLink*>(pBase); 5536 if (pLink && pLink->GetDestArea().aStart == rDestRange.aStart) 5537 { 5538 if ( bUndo ) 5539 { 5540 if ( !nRemoved ) 5541 { 5542 // group all remove and the insert action 5543 OUString aUndo = ScResId( STR_UNDO_INSERTAREALINK ); 5544 ViewShellId nViewShellId(-1); 5545 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) 5546 nViewShellId = pViewSh->GetViewShellId(); 5547 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId ); 5548 } 5549 5550 ScAreaLink* pOldArea = static_cast<ScAreaLink*>(pBase); 5551 rDocShell.GetUndoManager()->AddUndoAction( 5552 std::make_unique<ScUndoRemoveAreaLink>( &rDocShell, 5553 pOldArea->GetFile(), pOldArea->GetFilter(), pOldArea->GetOptions(), 5554 pOldArea->GetSource(), pOldArea->GetDestArea(), pOldArea->GetRefreshDelaySeconds() ) ); 5555 } 5556 pLinkManager->Remove( pBase ); 5557 nLinkCount = pLinkManager->GetLinks().size(); 5558 ++nRemoved; 5559 } 5560 else 5561 ++nLinkPos; 5562 } 5563 5564 OUString aFilterName = rFilter; 5565 OUString aNewOptions = rOptions; 5566 if (aFilterName.isEmpty()) 5567 ScDocumentLoader::GetFilterName( rFile, aFilterName, aNewOptions, true, !bApi ); 5568 5569 // remove application prefix from filter name here, so the filter options 5570 // aren't reset when the filter name is changed in ScAreaLink::DataChanged 5571 ScDocumentLoader::RemoveAppPrefix( aFilterName ); 5572 5573 ScAreaLink* pLink = new ScAreaLink( &rDocShell, rFile, aFilterName, 5574 aNewOptions, rSource, rDestRange, nRefreshDelaySeconds ); 5575 OUString aTmp = aFilterName; 5576 pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, rFile, &aTmp, &rSource ); 5577 5578 // Undo for an empty link 5579 5580 if (bUndo) 5581 { 5582 rDocShell.GetUndoManager()->AddUndoAction( std::make_unique<ScUndoInsertAreaLink>( &rDocShell, 5583 rFile, aFilterName, aNewOptions, 5584 rSource, rDestRange, nRefreshDelaySeconds ) ); 5585 if ( nRemoved ) 5586 rDocShell.GetUndoManager()->LeaveListAction(); // undo for link update is still separate 5587 } 5588 5589 // Update has its own undo 5590 if (rDoc.IsExecuteLinkEnabled()) 5591 { 5592 pLink->SetDoInsert(bFitBlock); // if applicable, don't insert anything on first update 5593 pLink->Update(); // no SetInCreate -> carry out update 5594 } 5595 pLink->SetDoInsert(true); // Default = true 5596 5597 SfxBindings* pBindings = rDocShell.GetViewBindings(); 5598 if (pBindings) 5599 pBindings->Invalidate( SID_LINKS ); 5600 5601 SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); // Navigator 5602 } 5603 5604 void ScDocFunc::ReplaceConditionalFormat( sal_uLong nOldFormat, std::unique_ptr<ScConditionalFormat> pFormat, SCTAB nTab, const ScRangeList& rRanges ) 5605 { 5606 ScDocShellModificator aModificator(rDocShell); 5607 ScDocument& rDoc = rDocShell.GetDocument(); 5608 if(rDoc.IsTabProtected(nTab)) 5609 return; 5610 5611 bool bUndo = rDoc.IsUndoEnabled(); 5612 ScDocumentUniquePtr pUndoDoc; 5613 ScRange aCombinedRange = rRanges.Combine(); 5614 ScRange aCompleteRange; 5615 if(bUndo) 5616 { 5617 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 5618 pUndoDoc->InitUndo( rDoc, nTab, nTab ); 5619 5620 if(pFormat) 5621 { 5622 aCompleteRange = aCombinedRange; 5623 } 5624 if(nOldFormat) 5625 { 5626 ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat); 5627 if(pOldFormat) 5628 aCompleteRange.ExtendTo(pOldFormat->GetRange().Combine()); 5629 } 5630 5631 rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab, 5632 aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab, 5633 InsertDeleteFlags::ALL, false, *pUndoDoc); 5634 } 5635 5636 std::unique_ptr<ScRange> pRepaintRange; 5637 if(nOldFormat) 5638 { 5639 ScConditionalFormat* pOldFormat = rDoc.GetCondFormList(nTab)->GetFormat(nOldFormat); 5640 if(pOldFormat) 5641 { 5642 pRepaintRange.reset(new ScRange( pOldFormat->GetRange().Combine() )); 5643 rDoc.RemoveCondFormatData(pOldFormat->GetRange(), nTab, pOldFormat->GetKey()); 5644 } 5645 5646 rDoc.DeleteConditionalFormat(nOldFormat, nTab); 5647 rDoc.SetStreamValid(nTab, false); 5648 } 5649 if(pFormat) 5650 { 5651 if(pRepaintRange) 5652 pRepaintRange->ExtendTo(aCombinedRange); 5653 else 5654 pRepaintRange.reset(new ScRange(aCombinedRange)); 5655 5656 sal_uLong nIndex = rDoc.AddCondFormat(std::move(pFormat), nTab); 5657 5658 rDoc.AddCondFormatData(rRanges, nTab, nIndex); 5659 rDoc.SetStreamValid(nTab, false); 5660 } 5661 5662 if(bUndo) 5663 { 5664 ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO)); 5665 pRedoDoc->InitUndo( rDoc, nTab, nTab ); 5666 rDoc.CopyToDocument(aCompleteRange.aStart.Col(),aCompleteRange.aStart.Row(),nTab, 5667 aCompleteRange.aEnd.Col(),aCompleteRange.aEnd.Row(),nTab, 5668 InsertDeleteFlags::ALL, false, *pRedoDoc); 5669 rDocShell.GetUndoManager()->AddUndoAction( 5670 std::make_unique<ScUndoConditionalFormat>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), aCompleteRange)); 5671 } 5672 5673 if(pRepaintRange) 5674 rDocShell.PostPaint(*pRepaintRange, PaintPartFlags::Grid, SC_PF_TESTMERGE); 5675 5676 aModificator.SetDocumentModified(); 5677 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged)); 5678 } 5679 5680 void ScDocFunc::SetConditionalFormatList( ScConditionalFormatList* pList, SCTAB nTab ) 5681 { 5682 ScDocShellModificator aModificator(rDocShell); 5683 ScDocument& rDoc = rDocShell.GetDocument(); 5684 if(rDoc.IsTabProtected(nTab)) 5685 return; 5686 5687 bool bUndo = rDoc.IsUndoEnabled(); 5688 ScDocumentUniquePtr pUndoDoc; 5689 if (bUndo) 5690 { 5691 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 5692 pUndoDoc->InitUndo( rDoc, nTab, nTab ); 5693 5694 ScConditionalFormatList* pOld = rDoc.GetCondFormList(nTab); 5695 5696 if (pOld) 5697 pUndoDoc->SetCondFormList(new ScConditionalFormatList(*pUndoDoc, *pOld), nTab); 5698 else 5699 pUndoDoc->SetCondFormList(nullptr, nTab); 5700 5701 } 5702 5703 // first remove all old entries 5704 ScConditionalFormatList* pOldList = rDoc.GetCondFormList(nTab); 5705 pOldList->RemoveFromDocument(rDoc); 5706 5707 // then set new entries 5708 pList->AddToDocument(rDoc); 5709 5710 rDoc.SetCondFormList(pList, nTab); 5711 rDocShell.PostPaintGridAll(); 5712 5713 if(bUndo) 5714 { 5715 ScDocumentUniquePtr pRedoDoc(new ScDocument(SCDOCMODE_UNDO)); 5716 pRedoDoc->InitUndo( rDoc, nTab, nTab ); 5717 pRedoDoc->SetCondFormList(new ScConditionalFormatList(*pRedoDoc, *pList), nTab); 5718 5719 rDocShell.GetUndoManager()->AddUndoAction( 5720 std::make_unique<ScUndoConditionalFormatList>(&rDocShell, std::move(pUndoDoc), std::move(pRedoDoc), nTab)); 5721 } 5722 5723 rDoc.SetStreamValid(nTab, false); 5724 aModificator.SetDocumentModified(); 5725 SfxGetpApp()->Broadcast(SfxHint(SfxHintId::ScAreasChanged)); 5726 } 5727 5728 void ScDocFunc::ConvertFormulaToValue( const ScRange& rRange, bool bInteraction ) 5729 { 5730 ScDocShellModificator aModificator(rDocShell); 5731 ScDocument& rDoc = rDocShell.GetDocument(); 5732 bool bRecord = true; 5733 if (!rDoc.IsUndoEnabled()) 5734 bRecord = false; 5735 5736 ScEditableTester aTester(rDoc, rRange); 5737 if (!aTester.IsEditable()) 5738 { 5739 if (bInteraction) 5740 rDocShell.ErrorMessage(aTester.GetMessageId()); 5741 return; 5742 } 5743 5744 sc::TableValues aUndoVals(rRange); 5745 sc::TableValues* pUndoVals = bRecord ? &aUndoVals : nullptr; 5746 5747 rDoc.ConvertFormulaToValue(rRange, pUndoVals); 5748 5749 if (bRecord && pUndoVals) 5750 { 5751 rDocShell.GetUndoManager()->AddUndoAction( 5752 std::make_unique<sc::UndoFormulaToValue>(&rDocShell, *pUndoVals)); 5753 } 5754 5755 rDocShell.PostPaint(rRange, PaintPartFlags::Grid); 5756 rDocShell.PostDataChanged(); 5757 rDoc.BroadcastCells(rRange, SfxHintId::ScDataChanged); 5758 aModificator.SetDocumentModified(); 5759 } 5760 5761 void ScDocFunc::EnterListAction(TranslateId pNameResId) 5762 { 5763 OUString aUndo(ScResId(pNameResId)); 5764 ViewShellId nViewShellId(-1); 5765 if (ScTabViewShell* pViewSh = ScTabViewShell::GetActiveViewShell()) 5766 nViewShellId = pViewSh->GetViewShellId(); 5767 rDocShell.GetUndoManager()->EnterListAction( aUndo, aUndo, 0, nViewShellId ); 5768 } 5769 5770 void ScDocFunc::EndListAction() 5771 { 5772 rDocShell.GetUndoManager()->LeaveListAction(); 5773 } 5774 5775 bool ScDocFunc::InsertSparklines(ScRange const& rDataRange, ScRange const& rSparklineRange, 5776 const std::shared_ptr<sc::SparklineGroup>& pSparklineGroup) 5777 { 5778 std::vector<sc::SparklineData> aSparklineDataVector; 5779 5780 if (rSparklineRange.aStart.Col() == rSparklineRange.aEnd.Col()) 5781 { 5782 sal_Int32 nOutputRowSize = rSparklineRange.aEnd.Row() - rSparklineRange.aStart.Row(); 5783 5784 auto eInputOrientation = sc::calculateOrientation(nOutputRowSize, rDataRange); 5785 5786 if (eInputOrientation == sc::RangeOrientation::Unknown) 5787 return false; 5788 5789 sal_Int32 nIndex = 0; 5790 5791 for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Row() <= rSparklineRange.aEnd.Row(); 5792 aAddress.IncRow()) 5793 { 5794 ScRange aInputRangeSlice = rDataRange; 5795 if (eInputOrientation == sc::RangeOrientation::Row) 5796 { 5797 aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex); 5798 aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex); 5799 } 5800 else 5801 { 5802 aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex); 5803 aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex); 5804 } 5805 5806 aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice); 5807 5808 nIndex++; 5809 } 5810 } 5811 else if (rSparklineRange.aStart.Row() == rSparklineRange.aEnd.Row()) 5812 { 5813 sal_Int32 nOutputColSize = rSparklineRange.aEnd.Col() - rSparklineRange.aStart.Col(); 5814 5815 auto eInputOrientation = sc::calculateOrientation(nOutputColSize, rDataRange); 5816 5817 if (eInputOrientation == sc::RangeOrientation::Unknown) 5818 return false; 5819 5820 sal_Int32 nIndex = 0; 5821 5822 for (ScAddress aAddress = rSparklineRange.aStart; aAddress.Col() <= rSparklineRange.aEnd.Col(); 5823 aAddress.IncCol()) 5824 { 5825 ScRange aInputRangeSlice = rDataRange; 5826 if (eInputOrientation == sc::RangeOrientation::Row) 5827 { 5828 aInputRangeSlice.aStart.SetRow(rDataRange.aStart.Row() + nIndex); 5829 aInputRangeSlice.aEnd.SetRow(rDataRange.aStart.Row() + nIndex); 5830 } 5831 else 5832 { 5833 aInputRangeSlice.aStart.SetCol(rDataRange.aStart.Col() + nIndex); 5834 aInputRangeSlice.aEnd.SetCol(rDataRange.aStart.Col() + nIndex); 5835 } 5836 5837 aSparklineDataVector.emplace_back(aAddress, aInputRangeSlice); 5838 5839 nIndex++; 5840 } 5841 } 5842 5843 if (aSparklineDataVector.empty()) 5844 return false; 5845 5846 auto pUndoInsertSparkline = std::make_unique<sc::UndoInsertSparkline>(rDocShell, aSparklineDataVector, pSparklineGroup); 5847 // insert the sparkline by "redoing" 5848 pUndoInsertSparkline->Redo(); 5849 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoInsertSparkline)); 5850 5851 return true; 5852 } 5853 5854 bool ScDocFunc::DeleteSparkline(ScAddress const& rAddress) 5855 { 5856 auto& rDocument = rDocShell.GetDocument(); 5857 5858 if (!rDocument.HasSparkline(rAddress)) 5859 return false; 5860 5861 auto pUndoDeleteSparkline = std::make_unique<sc::UndoDeleteSparkline>(rDocShell, rAddress); 5862 // delete sparkline by "redoing" 5863 pUndoDeleteSparkline->Redo(); 5864 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndoDeleteSparkline)); 5865 5866 return true; 5867 } 5868 5869 bool ScDocFunc::DeleteSparklineGroup(std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup, SCTAB nTab) 5870 { 5871 if (!pSparklineGroup) 5872 return false; 5873 5874 auto& rDocument = rDocShell.GetDocument(); 5875 5876 if (!rDocument.HasTable(nTab)) 5877 return false; 5878 5879 auto pUndo = std::make_unique<sc::UndoDeleteSparklineGroup>(rDocShell, pSparklineGroup, nTab); 5880 // delete sparkline group by "redoing" 5881 pUndo->Redo(); 5882 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); 5883 return true; 5884 } 5885 5886 bool ScDocFunc::ChangeSparklineGroupAttributes(std::shared_ptr<sc::SparklineGroup> const& pExistingSparklineGroup, 5887 sc::SparklineAttributes const& rNewAttributes) 5888 { 5889 auto pUndo = std::make_unique<sc::UndoEditSparklneGroup>(rDocShell, pExistingSparklineGroup, rNewAttributes); 5890 // change sparkline group attributes by "redoing" 5891 pUndo->Redo(); 5892 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); 5893 return true; 5894 } 5895 5896 bool ScDocFunc::GroupSparklines(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup> const& rpGroup) 5897 { 5898 auto pUndo = std::make_unique<sc::UndoGroupSparklines>(rDocShell, rRange, rpGroup); 5899 // group sparklines by "redoing" 5900 pUndo->Redo(); 5901 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); 5902 return true; 5903 } 5904 5905 bool ScDocFunc::UngroupSparklines(ScRange const& rRange) 5906 { 5907 auto pUndo = std::make_unique<sc::UndoUngroupSparklines>(rDocShell, rRange); 5908 // ungroup sparklines by "redoing" 5909 pUndo->Redo(); 5910 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); 5911 return true; 5912 } 5913 5914 bool ScDocFunc::ChangeSparkline(std::shared_ptr<sc::Sparkline> const& rpSparkline, SCTAB nTab, ScRangeList const& rDataRange) 5915 { 5916 auto pUndo = std::make_unique<sc::UndoEditSparkline>(rDocShell, rpSparkline, nTab, rDataRange); 5917 // change sparkline by "redoing" 5918 pUndo->Redo(); 5919 rDocShell.GetUndoManager()->AddUndoAction(std::move(pUndo)); 5920 return true; 5921 } 5922 5923 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 5924
