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 <com/sun/star/uno/Reference.hxx> 21 #include <com/sun/star/chart/XChartDocument.hpp> 22 #include <com/sun/star/chart2/XChartDocument.hpp> 23 #include <com/sun/star/embed/XClassifiedObject.hpp> 24 25 #include <scitems.hxx> 26 #include <editeng/eeitem.hxx> 27 #include <editeng/frmdiritem.hxx> 28 #include <sot/exchange.hxx> 29 #include <svx/objfac3d.hxx> 30 #include <svx/xtable.hxx> 31 #include <svx/svdoutl.hxx> 32 #include <svx/svditer.hxx> 33 #include <svx/svdlayer.hxx> 34 #include <svx/svdocapt.hxx> 35 #include <svx/svdograf.hxx> 36 #include <svx/svdoole2.hxx> 37 #include <svx/svdundo.hxx> 38 #include <svx/sdsxyitm.hxx> 39 #include <svx/svxids.hrc> 40 #include <i18nlangtag/mslangid.hxx> 41 #include <editeng/unolingu.hxx> 42 #include <svx/drawitem.hxx> 43 #include <editeng/fhgtitem.hxx> 44 #include <editeng/scriptspaceitem.hxx> 45 #include <sfx2/objsh.hxx> 46 #include <svl/itempool.hxx> 47 #include <vcl/canvastools.hxx> 48 #include <vcl/svapp.hxx> 49 #include <vcl/settings.hxx> 50 #include <tools/globname.hxx> 51 52 #include <basegfx/polygon/b2dpolygon.hxx> 53 #include <basegfx/polygon/b2dpolygontools.hxx> 54 55 #include <drwlayer.hxx> 56 #include <drawpage.hxx> 57 #include <global.hxx> 58 #include <document.hxx> 59 #include <userdat.hxx> 60 #include <markdata.hxx> 61 #include <globstr.hrc> 62 #include <scresid.hxx> 63 #include <scmod.hxx> 64 #include <postit.hxx> 65 #include <attrib.hxx> 66 #include <charthelper.hxx> 67 #include <table.hxx> 68 #include <basegfx/matrix/b2dhommatrix.hxx> 69 70 #include <vcl/field.hxx> 71 #include <memory> 72 73 namespace com::sun::star::embed { class XEmbeddedObject; } 74 75 #define DET_ARROW_OFFSET 1000 76 77 using namespace ::com::sun::star; 78 79 static E3dObjFactory* pF3d = nullptr; 80 static sal_uInt16 nInst = 0; 81 82 SfxObjectShell* ScDrawLayer::pGlobalDrawPersist = nullptr; 83 84 bool bDrawIsInUndo = false; //TODO: Member 85 86 ScUndoObjData::ScUndoObjData( SdrObject* pObjP, const ScAddress& rOS, const ScAddress& rOE, 87 const ScAddress& rNS, const ScAddress& rNE ) : 88 SdrUndoObj( *pObjP ), 89 aOldStt( rOS ), 90 aOldEnd( rOE ), 91 aNewStt( rNS ), 92 aNewEnd( rNE ) 93 { 94 } 95 96 ScUndoObjData::~ScUndoObjData() 97 { 98 } 99 100 void ScUndoObjData::Undo() 101 { 102 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj ); 103 OSL_ENSURE(pData,"ScUndoObjData: Data missing"); 104 if (pData) 105 { 106 pData->maStart = aOldStt; 107 pData->maEnd = aOldEnd; 108 } 109 110 // Undo also an untransformed anchor 111 pData = ScDrawLayer::GetNonRotatedObjData( pObj ); 112 if (pData) 113 { 114 pData->maStart = aOldStt; 115 pData->maEnd = aOldEnd; 116 } 117 } 118 119 void ScUndoObjData::Redo() 120 { 121 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj ); 122 OSL_ENSURE(pData,"ScUndoObjData: Data missing"); 123 if (pData) 124 { 125 pData->maStart = aNewStt; 126 pData->maEnd = aNewEnd; 127 } 128 129 // Redo also an untransformed anchor 130 pData = ScDrawLayer::GetNonRotatedObjData( pObj ); 131 if (pData) 132 { 133 pData->maStart = aNewStt; 134 pData->maEnd = aNewEnd; 135 } 136 } 137 138 ScUndoAnchorData::ScUndoAnchorData( SdrObject* pObjP, ScDocument* pDoc, SCTAB nTab ) : 139 SdrUndoObj( *pObjP ), 140 mpDoc( pDoc ), 141 mnTab( nTab ) 142 { 143 mbWasCellAnchored = ScDrawLayer::IsCellAnchored( *pObjP ); 144 mbWasResizeWithCell = ScDrawLayer::IsResizeWithCell( *pObjP ); 145 } 146 147 ScUndoAnchorData::~ScUndoAnchorData() 148 { 149 } 150 151 void ScUndoAnchorData::Undo() 152 { 153 // Trigger Object Change 154 if (pObj->IsInserted() && pObj->getSdrPageFromSdrObject()) 155 { 156 SdrHint aHint(SdrHintKind::ObjectChange, *pObj); 157 pObj->getSdrModelFromSdrObject().Broadcast(aHint); 158 } 159 160 if (mbWasCellAnchored) 161 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *mpDoc, mnTab, mbWasResizeWithCell); 162 else 163 ScDrawLayer::SetPageAnchored( *pObj ); 164 } 165 166 void ScUndoAnchorData::Redo() 167 { 168 if (mbWasCellAnchored) 169 ScDrawLayer::SetPageAnchored( *pObj ); 170 else 171 ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *mpDoc, mnTab, mbWasResizeWithCell); 172 173 // Trigger Object Change 174 if (pObj->IsInserted() && pObj->getSdrPageFromSdrObject()) 175 { 176 SdrHint aHint(SdrHintKind::ObjectChange, *pObj); 177 pObj->getSdrModelFromSdrObject().Broadcast(aHint); 178 } 179 } 180 181 ScTabDeletedHint::ScTabDeletedHint( SCTAB nTabNo ) : 182 nTab( nTabNo ) 183 { 184 } 185 186 ScTabDeletedHint::~ScTabDeletedHint() 187 { 188 } 189 190 ScTabSizeChangedHint::ScTabSizeChangedHint( SCTAB nTabNo ) : 191 nTab( nTabNo ) 192 { 193 } 194 195 ScTabSizeChangedHint::~ScTabSizeChangedHint() 196 { 197 } 198 199 #define MAXMM 10000000 200 201 static long TwipsToHmm (long nVal) 202 { 203 return static_cast< long >( MetricField::ConvertDoubleValue (static_cast<sal_Int64>(nVal), 0, 0, 204 FieldUnit::TWIP, FieldUnit::MM_100TH) ); 205 } 206 207 static long HmmToTwips (long nVal) 208 { 209 return static_cast< long > ( MetricField::ConvertDoubleValue (static_cast<sal_Int64>(nVal), 0, 0, 210 FieldUnit::MM_100TH, FieldUnit::TWIP) ); 211 } 212 213 static void lcl_ReverseTwipsToMM( tools::Rectangle& rRect ) 214 { 215 rRect.SetLeft( HmmToTwips( rRect.Left() ) ); 216 rRect.SetRight( HmmToTwips( rRect.Right() ) ); 217 rRect.SetTop( HmmToTwips( rRect.Top()) ); 218 rRect.SetBottom( HmmToTwips(rRect.Bottom()) ); 219 } 220 221 static ScRange lcl_getClipRangeFromClipDoc(ScDocument* pClipDoc, SCTAB nClipTab) 222 { 223 if (!pClipDoc) 224 return ScRange(); 225 226 SCCOL nClipStartX; 227 SCROW nClipStartY; 228 SCCOL nClipEndX; 229 SCROW nClipEndY; 230 pClipDoc->GetClipStart(nClipStartX, nClipStartY); 231 pClipDoc->GetClipArea(nClipEndX, nClipEndY, true); 232 nClipEndX = nClipEndX + nClipStartX; 233 nClipEndY += nClipStartY; // GetClipArea returns the difference 234 235 return ScRange(nClipStartX, nClipStartY, nClipTab, nClipEndX, nClipEndY, nClipTab); 236 } 237 238 ScDrawLayer::ScDrawLayer( ScDocument* pDocument, const OUString& rName ) : 239 FmFormModel( 240 nullptr, 241 pGlobalDrawPersist ? pGlobalDrawPersist : (pDocument ? pDocument->GetDocumentShell() : nullptr)), 242 aName( rName ), 243 pDoc( pDocument ), 244 bRecording( false ), 245 bAdjustEnabled( true ), 246 bHyphenatorSet( false ) 247 { 248 pGlobalDrawPersist = nullptr; // Only use once 249 250 SfxObjectShell* pObjSh = pDocument ? pDocument->GetDocumentShell() : nullptr; 251 XColorListRef pXCol = XColorList::GetStdColorList(); 252 if ( pObjSh ) 253 { 254 SetObjectShell( pObjSh ); 255 256 // set color table 257 const SvxColorListItem* pColItem = pObjSh->GetItem( SID_COLOR_TABLE ); 258 if ( pColItem ) 259 pXCol = pColItem->GetColorList(); 260 } 261 SetPropertyList( static_cast<XPropertyList *> (pXCol.get()) ); 262 263 SetSwapGraphics(); 264 265 SetScaleUnit(MapUnit::Map100thMM); 266 SfxItemPool& rPool = GetItemPool(); 267 rPool.SetDefaultMetric(MapUnit::Map100thMM); 268 SvxFrameDirectionItem aModeItem( SvxFrameDirection::Environment, EE_PARA_WRITINGDIR ); 269 rPool.SetPoolDefaultItem( aModeItem ); 270 271 // #i33700# 272 // Set shadow distance defaults as PoolDefaultItems. Details see bug. 273 rPool.SetPoolDefaultItem(makeSdrShadowXDistItem(300)); 274 rPool.SetPoolDefaultItem(makeSdrShadowYDistItem(300)); 275 276 // default for script spacing depends on locale, see SdDrawDocument ctor in sd 277 LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); 278 if (MsLangId::isKorean(eOfficeLanguage) || eOfficeLanguage == LANGUAGE_JAPANESE) 279 { 280 // secondary is edit engine pool 281 rPool.GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) ); 282 } 283 284 rPool.FreezeIdRanges(); // the pool is also used directly 285 286 SdrLayerAdmin& rAdmin = GetLayerAdmin(); 287 rAdmin.NewLayer("vorne", sal_uInt8(SC_LAYER_FRONT)); 288 rAdmin.NewLayer("hinten", sal_uInt8(SC_LAYER_BACK)); 289 rAdmin.NewLayer("intern", sal_uInt8(SC_LAYER_INTERN)); 290 rAdmin.NewLayer("Controls", sal_uInt8(SC_LAYER_CONTROLS)); 291 rAdmin.SetControlLayerName("Controls"); 292 rAdmin.NewLayer("hidden", sal_uInt8(SC_LAYER_HIDDEN)); 293 // "Controls" is new - must also be created when loading 294 295 // Set link for URL-Fields 296 ScModule* pScMod = SC_MOD(); 297 Outliner& rOutliner = GetDrawOutliner(); 298 rOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); 299 300 Outliner& rHitOutliner = GetHitTestOutliner(); 301 rHitOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); 302 303 // set FontHeight pool defaults without changing static SdrEngineDefaults 304 SfxItemPool* pOutlinerPool = rOutliner.GetEditTextObjectPool(); 305 if ( pOutlinerPool ) 306 { 307 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt 308 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt 309 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt 310 } 311 SfxItemPool* pHitOutlinerPool = rHitOutliner.GetEditTextObjectPool(); 312 if ( pHitOutlinerPool ) 313 { 314 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt 315 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt 316 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt 317 } 318 319 // initial undo mode as in Calc document 320 if( pDoc ) 321 EnableUndo( pDoc->IsUndoEnabled() ); 322 323 // URL-Buttons have no handler anymore, all is done by themselves 324 325 if( !nInst++ ) 326 { 327 pF3d = new E3dObjFactory; 328 } 329 } 330 331 ScDrawLayer::~ScDrawLayer() 332 { 333 Broadcast(SdrHint(SdrHintKind::ModelCleared)); 334 335 ClearModel(true); 336 337 pUndoGroup.reset(); 338 if( !--nInst ) 339 { 340 delete pF3d; 341 pF3d = nullptr; 342 } 343 } 344 345 void ScDrawLayer::UseHyphenator() 346 { 347 if (!bHyphenatorSet) 348 { 349 css::uno::Reference< css::linguistic2::XHyphenator > 350 xHyphenator = LinguMgr::GetHyphenator(); 351 352 GetDrawOutliner().SetHyphenator( xHyphenator ); 353 GetHitTestOutliner().SetHyphenator( xHyphenator ); 354 355 bHyphenatorSet = true; 356 } 357 } 358 359 SdrPage* ScDrawLayer::AllocPage(bool bMasterPage) 360 { 361 return new ScDrawPage(*this, bMasterPage); 362 } 363 364 bool ScDrawLayer::HasObjects() const 365 { 366 bool bFound = false; 367 368 sal_uInt16 nCount = GetPageCount(); 369 for (sal_uInt16 i=0; i<nCount && !bFound; i++) 370 if (GetPage(i)->GetObjCount()) 371 bFound = true; 372 373 return bFound; 374 } 375 376 SdrModel* ScDrawLayer::AllocModel() const 377 { 378 // Allocated model (for clipboard etc) must not have a pointer 379 // to the original model's document, pass NULL as document: 380 381 return new ScDrawLayer( nullptr, aName ); 382 } 383 384 bool ScDrawLayer::ScAddPage( SCTAB nTab ) 385 { 386 if (bDrawIsInUndo) 387 return false; // not inserted 388 389 ScDrawPage* pPage = static_cast<ScDrawPage*>(AllocPage( false )); 390 InsertPage(pPage, static_cast<sal_uInt16>(nTab)); 391 if (bRecording) 392 AddCalcUndo(std::make_unique<SdrUndoNewPage>(*pPage)); 393 394 ResetTab(nTab, pDoc->GetTableCount()-1); 395 return true; // inserted 396 } 397 398 void ScDrawLayer::ScRemovePage( SCTAB nTab ) 399 { 400 if (bDrawIsInUndo) 401 return; 402 403 Broadcast( ScTabDeletedHint( nTab ) ); 404 if (bRecording) 405 { 406 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 407 AddCalcUndo(std::make_unique<SdrUndoDelPage>(*pPage)); // Undo-Action becomes the page owner 408 RemovePage( static_cast<sal_uInt16>(nTab) ); // just deliver, not deleting 409 } 410 else 411 DeletePage( static_cast<sal_uInt16>(nTab) ); // just get rid of it 412 413 ResetTab(nTab, pDoc->GetTableCount()-1); 414 } 415 416 void ScDrawLayer::ScRenamePage( SCTAB nTab, const OUString& rNewName ) 417 { 418 ScDrawPage* pPage = static_cast<ScDrawPage*>( GetPage(static_cast<sal_uInt16>(nTab)) ); 419 if (pPage) 420 pPage->SetName(rNewName); 421 } 422 423 void ScDrawLayer::ScMovePage( sal_uInt16 nOldPos, sal_uInt16 nNewPos ) 424 { 425 MovePage( nOldPos, nNewPos ); 426 sal_uInt16 nMinPos = std::min(nOldPos, nNewPos); 427 ResetTab(nMinPos, pDoc->GetTableCount()-1); 428 } 429 430 void ScDrawLayer::ScCopyPage( sal_uInt16 nOldPos, sal_uInt16 nNewPos ) 431 { 432 if (bDrawIsInUndo) 433 return; 434 435 SdrPage* pOldPage = GetPage(nOldPos); 436 SdrPage* pNewPage = GetPage(nNewPos); 437 438 // Copying 439 440 if (pOldPage && pNewPage) 441 { 442 SCTAB nOldTab = static_cast<SCTAB>(nOldPos); 443 SCTAB nNewTab = static_cast<SCTAB>(nNewPos); 444 445 SdrObjListIter aIter( pOldPage, SdrIterMode::Flat ); 446 SdrObject* pOldObject = aIter.Next(); 447 while (pOldObject) 448 { 449 ScDrawObjData* pOldData = GetObjData(pOldObject); 450 if (pOldData) 451 { 452 pOldData->maStart.SetTab(nOldTab); 453 pOldData->maEnd.SetTab(nOldTab); 454 } 455 456 // Clone to target SdrModel 457 SdrObject* pNewObject(pOldObject->CloneSdrObject(*this)); 458 pNewObject->NbcMove(Size(0,0)); 459 pNewPage->InsertObject( pNewObject ); 460 ScDrawObjData* pNewData = GetObjData(pNewObject); 461 if (pNewData) 462 { 463 pNewData->maStart.SetTab(nNewTab); 464 pNewData->maEnd.SetTab(nNewTab); 465 } 466 467 if (bRecording) 468 AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) ); 469 470 pOldObject = aIter.Next(); 471 } 472 } 473 474 ResetTab(static_cast<SCTAB>(nNewPos), pDoc->GetTableCount()-1); 475 } 476 477 void ScDrawLayer::ResetTab( SCTAB nStart, SCTAB nEnd ) 478 { 479 SCTAB nPageSize = static_cast<SCTAB>(GetPageCount()); 480 if (nPageSize < 0) 481 // No drawing pages exist. 482 return; 483 484 if (nEnd >= nPageSize) 485 // Avoid iterating beyond the last existing page. 486 nEnd = nPageSize - 1; 487 488 for (SCTAB i = nStart; i <= nEnd; ++i) 489 { 490 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(i)); 491 if (!pPage) 492 continue; 493 494 SdrObjListIter aIter(pPage, SdrIterMode::Flat); 495 for (SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next()) 496 { 497 ScDrawObjData* pData = GetObjData(pObj); 498 if (!pData) 499 continue; 500 501 pData->maStart.SetTab(i); 502 pData->maEnd.SetTab(i); 503 } 504 } 505 } 506 507 static bool IsInBlock( const ScAddress& rPos, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2 ) 508 { 509 return rPos.Col() >= nCol1 && rPos.Col() <= nCol2 && 510 rPos.Row() >= nRow1 && rPos.Row() <= nRow2; 511 } 512 513 void ScDrawLayer::MoveCells( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, 514 SCCOL nDx,SCROW nDy, bool bUpdateNoteCaptionPos ) 515 { 516 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 517 OSL_ENSURE(pPage,"Page not found"); 518 if (!pPage) 519 return; 520 521 bool bNegativePage = pDoc && pDoc->IsNegativePage( nTab ); 522 523 const size_t nCount = pPage->GetObjCount(); 524 for ( size_t i = 0; i < nCount; ++i ) 525 { 526 SdrObject* pObj = pPage->GetObj( i ); 527 ScDrawObjData* pData = GetObjDataTab( pObj, nTab ); 528 if( pData ) 529 { 530 const ScAddress aOldStt = pData->maStart; 531 const ScAddress aOldEnd = pData->maEnd; 532 bool bChange = false; 533 if ( aOldStt.IsValid() && IsInBlock( aOldStt, nCol1,nRow1, nCol2,nRow2 ) ) 534 { 535 pData->maStart.IncCol( nDx ); 536 pData->maStart.IncRow( nDy ); 537 bChange = true; 538 } 539 if ( aOldEnd.IsValid() && IsInBlock( aOldEnd, nCol1,nRow1, nCol2,nRow2 ) ) 540 { 541 pData->maEnd.IncCol( nDx ); 542 pData->maEnd.IncRow( nDy ); 543 bChange = true; 544 } 545 if (bChange) 546 { 547 if ( dynamic_cast<const SdrRectObj*>( pObj) != nullptr && pData->maStart.IsValid() && pData->maEnd.IsValid() ) 548 pData->maStart.PutInOrder( pData->maEnd ); 549 550 // Update also an untransformed anchor that's what we stored ( and still do ) to xml 551 ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData( pObj ); 552 if ( pNoRotatedAnchor ) 553 { 554 pNoRotatedAnchor->maStart = pData->maStart; 555 pNoRotatedAnchor->maEnd = pData->maEnd; 556 } 557 558 AddCalcUndo( std::make_unique<ScUndoObjData>( pObj, aOldStt, aOldEnd, pData->maStart, pData->maEnd ) ); 559 RecalcPos( pObj, *pData, bNegativePage, bUpdateNoteCaptionPos ); 560 } 561 } 562 } 563 } 564 565 void ScDrawLayer::SetPageSize( sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos ) 566 { 567 SdrPage* pPage = GetPage(nPageNo); 568 if (pPage) 569 { 570 if ( rSize != pPage->GetSize() ) 571 { 572 pPage->SetSize( rSize ); 573 Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() on the views 574 } 575 576 // Implement Detective lines (adjust to new heights / widths) 577 // even if size is still the same 578 // (individual rows/columns can have been changed)) 579 580 bool bNegativePage = pDoc && pDoc->IsNegativePage( static_cast<SCTAB>(nPageNo) ); 581 582 // Disable mass broadcasts from drawing objects' position changes. 583 bool bWasLocked = isLocked(); 584 setLock(true); 585 const size_t nCount = pPage->GetObjCount(); 586 for ( size_t i = 0; i < nCount; ++i ) 587 { 588 SdrObject* pObj = pPage->GetObj( i ); 589 ScDrawObjData* pData = GetObjDataTab( pObj, static_cast<SCTAB>(nPageNo) ); 590 if( pData ) 591 RecalcPos( pObj, *pData, bNegativePage, bUpdateNoteCaptionPos ); 592 } 593 setLock(bWasLocked); 594 } 595 } 596 597 namespace 598 { 599 //Can't have a zero width dimension 600 tools::Rectangle lcl_makeSafeRectangle(const tools::Rectangle &rNew) 601 { 602 tools::Rectangle aRect = rNew; 603 if (aRect.Bottom() == aRect.Top()) 604 aRect.SetBottom( aRect.Top()+1 ); 605 if (aRect.Right() == aRect.Left()) 606 aRect.SetRight( aRect.Left()+1 ); 607 return aRect; 608 } 609 610 Point lcl_calcAvailableDiff(const ScDocument &rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const Point &aWantedDiff) 611 { 612 Point aAvailableDiff(aWantedDiff); 613 long nHeight = static_cast<long>(rDoc.GetRowHeight( nRow, nTab ) * HMM_PER_TWIPS); 614 long nWidth = static_cast<long>(rDoc.GetColWidth( nCol, nTab ) * HMM_PER_TWIPS); 615 if (aAvailableDiff.Y() > nHeight) 616 aAvailableDiff.setY( nHeight ); 617 if (aAvailableDiff.X() > nWidth) 618 aAvailableDiff.setX( nWidth ); 619 return aAvailableDiff; 620 } 621 622 tools::Rectangle lcl_UpdateCalcPoly(basegfx::B2DPolygon &rCalcPoly, int nWhichPoint, const Point &rPos) 623 { 624 rCalcPoly.setB2DPoint(nWhichPoint, basegfx::B2DPoint(rPos.X(), rPos.Y())); 625 basegfx::B2DRange aRange(basegfx::utils::getRange(rCalcPoly)); 626 return tools::Rectangle(static_cast<long>(aRange.getMinX()), static_cast<long>(aRange.getMinY()), 627 static_cast<long>(aRange.getMaxX()), static_cast<long>(aRange.getMaxY())); 628 } 629 } 630 631 void ScDrawLayer::ResizeLastRectFromAnchor(const SdrObject* pObj, ScDrawObjData& rData, 632 bool bUseLogicRect, bool bNegativePage, bool bCanResize, 633 bool bHiddenAsZero) 634 { 635 tools::Rectangle aRect = bUseLogicRect ? pObj->GetLogicRect() : pObj->GetSnapRect(); 636 SCCOL nCol1 = rData.maStart.Col(); 637 SCROW nRow1 = rData.maStart.Row(); 638 SCTAB nTab1 = rData.maStart.Tab(); 639 SCCOL nCol2 = rData.maEnd.Col(); 640 SCROW nRow2 = rData.maEnd.Row(); 641 SCTAB nTab2 = rData.maEnd.Tab(); 642 Point aPos(pDoc->GetColOffset(nCol1, nTab1, bHiddenAsZero), 643 pDoc->GetRowOffset(nRow1, nTab1, bHiddenAsZero)); 644 aPos.setX(TwipsToHmm(aPos.X())); 645 aPos.setY(TwipsToHmm(aPos.Y())); 646 aPos += lcl_calcAvailableDiff(*pDoc, nCol1, nRow1, nTab1, rData.maStartOffset); 647 648 // this sets the needed changed position (translation) 649 aRect.SetPos(aPos); 650 651 if (bCanResize) 652 { 653 // all this stuff is additional stuff to evtl. not only translate the 654 // range (Rectangle), but also check for and evtl. do corrections for it's size 655 const tools::Rectangle aLastCellRect(rData.getLastCellRect()); 656 657 // If the row was hidden before, or we don't have a valid cell rect, calculate the 658 // new rect based on the end point. 659 // Also when the end point is set, we need to consider it. 660 if (rData.mbWasInHiddenRow || aLastCellRect.IsEmpty() || nRow1 != nRow2 || nCol1 != nCol2) 661 { 662 Point aEnd(pDoc->GetColOffset(nCol2, nTab2, bHiddenAsZero), 663 pDoc->GetRowOffset(nRow2, nTab2, bHiddenAsZero)); 664 aEnd.setX(TwipsToHmm(aEnd.X())); 665 aEnd.setY(TwipsToHmm(aEnd.Y())); 666 aEnd += lcl_calcAvailableDiff(*pDoc, nCol2, nRow2, nTab2, rData.maEndOffset); 667 668 aRect = tools::Rectangle(aPos, aEnd); 669 } 670 else if (!aLastCellRect.IsEmpty()) 671 { 672 // We calculate based on the last cell rect to be able to scale the image 673 // as much as the cell was scaled. 674 // Still, we keep the image in its current cell (to keep start anchor == end anchor) 675 const tools::Rectangle aCurrentCellRect(GetCellRect(*GetDocument(), rData.maStart, true)); 676 long nCurrentWidth(aCurrentCellRect.GetWidth()); 677 long nCurrentHeight(aCurrentCellRect.GetHeight()); 678 const long nLastWidth(aLastCellRect.GetWidth()); 679 const long nLastHeight(aLastCellRect.GetHeight()); 680 681 // tdf#116931 Avoid and correct nifty numerical problems with the integer 682 // based and converted values (GetCellRect uses multiplies with HMM_PER_TWIPS) 683 if(nCurrentWidth + 1 == nLastWidth || nCurrentWidth == nLastWidth + 1) 684 { 685 nCurrentWidth = nLastWidth; 686 } 687 688 if(nCurrentHeight + 1 == nLastHeight || nCurrentHeight == nLastHeight + 1) 689 { 690 nCurrentHeight = nLastHeight; 691 } 692 693 // get initial ScalingFactors 694 double fWidthFactor(nCurrentWidth == nLastWidth || 0 == nLastWidth 695 ? 1.0 696 : static_cast<double>(nCurrentWidth) / static_cast<double>(nLastWidth)); 697 double fHeightFactor(nCurrentHeight == nLastHeight || 0 == nLastHeight 698 ? 1.0 699 : static_cast<double>(nCurrentHeight) / static_cast<double>(nLastHeight)); 700 701 // check if we grow or shrink - and at all 702 const bool bIsGrowing(nCurrentWidth > nLastWidth || nCurrentHeight > nLastHeight); 703 const bool bIsShrinking(nCurrentWidth < nLastWidth || nCurrentHeight < nLastHeight); 704 const bool bIsSizeChanged(bIsGrowing || bIsShrinking); 705 706 // handle AspectRatio, only needed if size does change 707 if(bIsSizeChanged && pObj->shouldKeepAspectRatio()) 708 { 709 tools::Rectangle aRectIncludingOffset = aRect; 710 aRectIncludingOffset.setWidth(aRect.GetWidth() + rData.maStartOffset.X()); 711 aRectIncludingOffset.setHeight(aRect.GetHeight() + rData.maStartOffset.Y()); 712 long nWidth = aRectIncludingOffset.GetWidth(); 713 assert(nWidth && "div-by-zero"); 714 double fMaxWidthFactor = static_cast<double>(nCurrentWidth) 715 / static_cast<double>(nWidth); 716 long nHeight = aRectIncludingOffset.GetHeight(); 717 assert(nHeight && "div-by-zero"); 718 double fMaxHeightFactor = static_cast<double>(nCurrentHeight) 719 / static_cast<double>(nHeight); 720 double fMaxFactor = std::min(fMaxHeightFactor, fMaxWidthFactor); 721 722 if(bIsGrowing) // cell is growing larger 723 { 724 // To actually grow the image, we need to take the max 725 fWidthFactor = std::max(fWidthFactor, fHeightFactor); 726 } 727 else if(bIsShrinking) // cell is growing smaller, take the min 728 { 729 fWidthFactor = std::min(fWidthFactor, fHeightFactor); 730 } 731 732 // We don't want the image to become larger than the current cell 733 fWidthFactor = fHeightFactor = std::min(fWidthFactor, fMaxFactor); 734 } 735 736 if(bIsSizeChanged) 737 { 738 // tdf#116931 re-organized scaling (if needed) 739 // Check if we need to scale at all. Always scale on growing. 740 bool bNeedToScale(bIsGrowing); 741 742 if(!bNeedToScale && bIsShrinking) 743 { 744 // Check if original still fits into space. Do *not* forget to 745 // compare with evtl. numerically corrected aCurrentCellRect 746 const bool bFitsInX(aRect.Right() <= aCurrentCellRect.Left() + nCurrentWidth); 747 const bool bFitsInY(aRect.Bottom() <= aCurrentCellRect.Top() + nCurrentHeight); 748 749 // If the image still fits in the smaller cell, don't resize it at all 750 bNeedToScale = (!bFitsInX || !bFitsInY); 751 } 752 753 if(bNeedToScale) 754 { 755 // tdf#116931 use transformations now. Translation is already applied 756 // (see aRect.SetPos above), so only scale needs to be applied - relative 757 // to *new* CellRect (which is aCurrentCellRect). 758 // Prepare scale relative to top-left of aCurrentCellRect 759 basegfx::B2DHomMatrix aChange; 760 761 aChange.translate(-aCurrentCellRect.getX(), -aCurrentCellRect.getY()); 762 aChange.scale(fWidthFactor, fHeightFactor); 763 aChange.translate(aCurrentCellRect.getX(), aCurrentCellRect.getY()); 764 765 // create B2DRange and transform by prepared scale 766 basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aRect); 767 768 aNewRange.transform(aChange); 769 770 // apply to aRect 771 aRect = tools::Rectangle( 772 basegfx::fround(aNewRange.getMinX()), basegfx::fround(aNewRange.getMinY()), 773 basegfx::fround(aNewRange.getMaxX()), basegfx::fround(aNewRange.getMaxY())); 774 } 775 } 776 } 777 } 778 779 if (bNegativePage) 780 MirrorRectRTL(aRect); 781 782 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect), pObj->IsVisible()); 783 } 784 785 void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegativePage, bool bUpdateNoteCaptionPos ) 786 { 787 OSL_ENSURE( pDoc, "ScDrawLayer::RecalcPos - missing document" ); 788 if( !pDoc ) 789 return; 790 791 if (rData.meType == ScDrawObjData::CellNote) 792 { 793 OSL_ENSURE( rData.maStart.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" ); 794 /* #i109372# On insert/remove rows/columns/cells: Updating the caption 795 position must not be done, if the cell containing the note has not 796 been moved yet in the document. The calling code now passes an 797 additional boolean stating if the cells are already moved. */ 798 if( bUpdateNoteCaptionPos ) 799 /* When inside an undo action, there may be pending note captions 800 where cell note is already deleted (thus document cannot find 801 the note object anymore). The caption will be deleted later 802 with drawing undo. */ 803 if( ScPostIt* pNote = pDoc->GetNote( rData.maStart ) ) 804 pNote->UpdateCaptionPos( rData.maStart ); 805 return; 806 } 807 808 bool bValid1 = rData.maStart.IsValid(); 809 SCCOL nCol1 = rData.maStart.Col(); 810 SCROW nRow1 = rData.maStart.Row(); 811 SCTAB nTab1 = rData.maStart.Tab(); 812 bool bValid2 = rData.maEnd.IsValid(); 813 SCCOL nCol2 = rData.maEnd.Col(); 814 SCROW nRow2 = rData.maEnd.Row(); 815 SCTAB nTab2 = rData.maEnd.Tab(); 816 817 if (rData.meType == ScDrawObjData::ValidationCircle) 818 { 819 // Validation circle for detective. 820 rData.setShapeRect(GetDocument(), pObj->GetLogicRect()); 821 822 Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); 823 aPos.setX(TwipsToHmm( aPos.X() )); 824 aPos.setY(TwipsToHmm( aPos.Y() )); 825 826 // Calculations and values as in detfunc.cxx 827 828 Size aSize( TwipsToHmm( pDoc->GetColWidth( nCol1, nTab1) ), 829 TwipsToHmm( pDoc->GetRowHeight( nRow1, nTab1) ) ); 830 tools::Rectangle aRect( aPos, aSize ); 831 aRect.AdjustLeft( -250 ); 832 aRect.AdjustRight(250 ); 833 aRect.AdjustTop( -70 ); 834 aRect.AdjustBottom(70 ); 835 if ( bNegativePage ) 836 MirrorRectRTL( aRect ); 837 838 if ( pObj->GetLogicRect() != aRect ) 839 { 840 if (bRecording) 841 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 842 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect)); 843 pObj->SetLogicRect(rData.getShapeRect()); 844 } 845 } 846 else if (rData.meType == ScDrawObjData::DetectiveArrow) 847 { 848 rData.setShapeRect(GetDocument(), pObj->GetLogicRect()); 849 basegfx::B2DPolygon aCalcPoly; 850 Point aOrigStartPos(pObj->GetPoint(0)); 851 Point aOrigEndPos(pObj->GetPoint(1)); 852 aCalcPoly.append(basegfx::B2DPoint(aOrigStartPos.X(), aOrigStartPos.Y())); 853 aCalcPoly.append(basegfx::B2DPoint(aOrigEndPos.X(), aOrigEndPos.Y())); 854 //TODO: do not create multiple Undos for one object (last one can be omitted then) 855 856 SCCOL nLastCol; 857 SCROW nLastRow; 858 if( bValid1 ) 859 { 860 Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); 861 if (!pDoc->ColHidden(nCol1, nTab1, nullptr, &nLastCol)) 862 aPos.AdjustX(pDoc->GetColWidth( nCol1, nTab1 ) / 4 ); 863 if (!pDoc->RowHidden(nRow1, nTab1, nullptr, &nLastRow)) 864 aPos.AdjustY(pDoc->GetRowHeight( nRow1, nTab1 ) / 2 ); 865 aPos.setX(TwipsToHmm( aPos.X() )); 866 aPos.setY(TwipsToHmm( aPos.Y() )); 867 Point aStartPos = aPos; 868 if ( bNegativePage ) 869 aStartPos.setX( -aStartPos.X() ); // don't modify aPos - used below 870 if ( pObj->GetPoint( 0 ) != aStartPos ) 871 { 872 if (bRecording) 873 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 874 875 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos)); 876 pObj->SetPoint( aStartPos, 0 ); 877 } 878 879 if( !bValid2 ) 880 { 881 Point aEndPos( aPos.X() + DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); 882 if (aEndPos.Y() < 0) 883 aEndPos.AdjustY(2 * DET_ARROW_OFFSET); 884 if ( bNegativePage ) 885 aEndPos.setX( -aEndPos.X() ); 886 if ( pObj->GetPoint( 1 ) != aEndPos ) 887 { 888 if (bRecording) 889 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 890 891 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos)); 892 pObj->SetPoint( aEndPos, 1 ); 893 } 894 } 895 } 896 if( bValid2 ) 897 { 898 Point aPos( pDoc->GetColOffset( nCol2, nTab2 ), pDoc->GetRowOffset( nRow2, nTab2 ) ); 899 if (!pDoc->ColHidden(nCol2, nTab2, nullptr, &nLastCol)) 900 aPos.AdjustX(pDoc->GetColWidth( nCol2, nTab2 ) / 4 ); 901 if (!pDoc->RowHidden(nRow2, nTab2, nullptr, &nLastRow)) 902 aPos.AdjustY(pDoc->GetRowHeight( nRow2, nTab2 ) / 2 ); 903 aPos.setX(TwipsToHmm( aPos.X() )); 904 aPos.setY(TwipsToHmm( aPos.Y() )); 905 Point aEndPos = aPos; 906 if ( bNegativePage ) 907 aEndPos.setX( -aEndPos.X() ); // don't modify aPos - used below 908 if ( pObj->GetPoint( 1 ) != aEndPos ) 909 { 910 if (bRecording) 911 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 912 913 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos)); 914 pObj->SetPoint( aEndPos, 1 ); 915 } 916 917 if( !bValid1 ) 918 { 919 Point aStartPos( aPos.X() - DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); 920 if (aStartPos.X() < 0) 921 aStartPos.AdjustX(2 * DET_ARROW_OFFSET); 922 if (aStartPos.Y() < 0) 923 aStartPos.AdjustY(2 * DET_ARROW_OFFSET); 924 if ( bNegativePage ) 925 aStartPos.setX( -aStartPos.X() ); 926 if ( pObj->GetPoint( 0 ) != aStartPos ) 927 { 928 if (bRecording) 929 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 930 931 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos)); 932 pObj->SetPoint( aStartPos, 0 ); 933 } 934 } 935 } 936 } 937 else 938 { 939 // Prevent multiple broadcasts during the series of changes. 940 bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked(); 941 pObj->getSdrModelFromSdrObject().setLock(true); 942 bool bCanResize = bValid2 && !pObj->IsResizeProtect() && rData.mbResizeWithCell; 943 944 //First time positioning, must be able to at least move it 945 ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData( pObj, true ); 946 if (rData.getShapeRect().IsEmpty()) 947 { 948 // Every shape it is saved with a negative offset relative to cell 949 ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj); 950 if (aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE) 951 { 952 // tdf#117145 All that TR*etBaseGeometry does here is to translate 953 // the existing transformation. This can simply be applied to the existing 954 // matrix, no need to decompose as done before. Also doing this from 955 // Calc did not change metrics in any way. 956 const tools::Rectangle aRect(pDoc->GetMMRect(nCol1, nRow1, nCol1 , nRow1, nTab1)); 957 const Point aPoint(bNegativePage ? aRect.Right() : aRect.Left(), aRect.Top()); 958 basegfx::B2DPolyPolygon aPolyPolygon; 959 basegfx::B2DHomMatrix aOriginalMatrix; 960 961 pObj->TRGetBaseGeometry(aOriginalMatrix, aPolyPolygon); 962 aOriginalMatrix.translate(aPoint.X(), aPoint.Y()); 963 pObj->TRSetBaseGeometry(aOriginalMatrix, aPolyPolygon); 964 } 965 966 // It's confusing ( but blame that we persist the anchor in terms of unrotated shape ) 967 // that the initial anchor we get here is in terms of an unrotated shape ( if the shape is rotated ) 968 // we need to save the old anchor ( for persisting ) and also track any resize or repositions that happen. 969 970 // This is an evil hack, having an anchor that is one minute in terms of untransformed object and then later 971 // in terms of the transformed object is not ideal, similarly having 2 anchors per object is wasteful, can't 972 // see another way out of this at the moment though. 973 rNoRotatedAnchor.maStart = rData.maStart; 974 rNoRotatedAnchor.maEnd = rData.maEnd; 975 rNoRotatedAnchor.maStartOffset = rData.maStartOffset; 976 rNoRotatedAnchor.maEndOffset = rData.maEndOffset; 977 978 // get bounding rectangle of shape ( include any hidden row/columns ), <sigh> we need to do this 979 // because if the shape is rotated the anchor from xml is in terms of the unrotated shape, if 980 // the shape is hidden ( by the rows that contain the shape being hidden ) then our hack of 981 // trying to infer the 'real' e.g. rotated anchor from the SnapRect will fail (because the LogicRect will 982 // not have the correct position or size). The only way we can possible do this is to first get the 983 // 'unrotated' shape dimensions from the persisted Anchor (from xml) and then 'create' an Anchor from the 984 // associated rotated shape (note: we do this by actually setting the LogicRect for the shape temporarily to the 985 // *full* size then grabbing the SnapRect (which gives the transformed rotated dimensions), it would be 986 // wonderful if we could do this mathematically without having to temporarily tweak the object... otoh this way 987 // is guaranteed to get consistent results) 988 ResizeLastRectFromAnchor( pObj, rData, true, bNegativePage, bCanResize, false ); 989 // aFullRect contains the unrotated size and position of the shape (regardless of any hidden row/columns) 990 tools::Rectangle aFullRect = rData.getShapeRect(); 991 992 // get current size and position from the anchor for use later 993 ResizeLastRectFromAnchor( pObj, rNoRotatedAnchor, true, bNegativePage, bCanResize ); 994 995 // resize/position the shape to *full* size e.g. how it would be ( if no hidden rows/cols affected things ) 996 pObj->SetLogicRect(aFullRect); 997 998 // Ok, here is more nastiness, from xml the Anchor is in terms of the LogicRect which is the 999 // untransformed unrotated shape, here we swap out that initial anchor and from now on use 1000 // an Anchor based on the SnapRect ( which is what you see on the screen ) 1001 const tools::Rectangle aObjRect(pObj->GetSnapRect()); 1002 ScDrawLayer::GetCellAnchorFromPosition( 1003 aObjRect, 1004 rData, 1005 *pDoc, 1006 nTab1, 1007 false); 1008 1009 // reset shape to true 'maybe affected by hidden rows/cols' size calculated previously 1010 pObj->SetLogicRect(rNoRotatedAnchor.getShapeRect()); 1011 } 1012 1013 // update anchor with snap rect 1014 ResizeLastRectFromAnchor( pObj, rData, false, bNegativePage, bCanResize ); 1015 1016 if( bCanResize ) 1017 { 1018 tools::Rectangle aNew = rData.getShapeRect(); 1019 1020 if ( pObj->GetSnapRect() != aNew ) 1021 { 1022 tools::Rectangle aOld(pObj->GetSnapRect()); 1023 1024 if (bRecording) 1025 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1026 long nOldWidth = aOld.GetWidth(); 1027 long nOldHeight = aOld.GetHeight(); 1028 if (pObj->IsPolyObj() && nOldWidth && nOldHeight) 1029 { 1030 // Polyline objects need special treatment. 1031 Size aSizeMove(aNew.Left()-aOld.Left(), aNew.Top()-aOld.Top()); 1032 pObj->NbcMove(aSizeMove); 1033 1034 double fXFrac = static_cast<double>(aNew.GetWidth()) / static_cast<double>(nOldWidth); 1035 double fYFrac = static_cast<double>(aNew.GetHeight()) / static_cast<double>(nOldHeight); 1036 pObj->NbcResize(aNew.TopLeft(), Fraction(fXFrac), Fraction(fYFrac)); 1037 } 1038 // order of these lines is important, modify rData.maLastRect carefully it is used as both 1039 // a value and a flag for initialisation 1040 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData.getShapeRect()), pObj->IsVisible()); 1041 if (pObj->GetObjIdentifier() == OBJ_CUSTOMSHAPE) 1042 pObj->AdjustToMaxRect(rData.getShapeRect()); 1043 else 1044 pObj->SetSnapRect(rData.getShapeRect()); 1045 // update 'unrotated anchor' it's the anchor we persist, it must be kept in sync 1046 // with the normal Anchor 1047 ResizeLastRectFromAnchor( pObj, rNoRotatedAnchor, true, bNegativePage, bCanResize ); 1048 } 1049 } 1050 else 1051 { 1052 Point aPos( rData.getShapeRect().getX(), rData.getShapeRect().getY() ); 1053 if ( pObj->GetRelativePos() != aPos ) 1054 { 1055 if (bRecording) 1056 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1057 pObj->SetRelativePos( aPos ); 1058 } 1059 } 1060 /* 1061 * If we were not allowed resize the object, then the end cell anchor 1062 * is possibly incorrect now, and if the object has no end-cell (e.g. 1063 * missing in original .xml) we are also forced to generate one 1064 */ 1065 bool bEndAnchorIsBad = !bValid2 || pObj->IsResizeProtect(); 1066 if (bEndAnchorIsBad) 1067 { 1068 // update 'rotated' anchor 1069 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rData, *pDoc, nTab1, false); 1070 // update 'unrotated' anchor 1071 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1 ); 1072 } 1073 1074 // End prevent multiple broadcasts during the series of changes. 1075 pObj->getSdrModelFromSdrObject().setLock(bWasLocked); 1076 if (!bWasLocked) 1077 pObj->BroadcastObjectChange(); 1078 } 1079 } 1080 1081 bool ScDrawLayer::GetPrintArea( ScRange& rRange, bool bSetHor, bool bSetVer ) const 1082 { 1083 OSL_ENSURE( pDoc, "ScDrawLayer::GetPrintArea without document" ); 1084 if ( !pDoc ) 1085 return false; 1086 1087 SCTAB nTab = rRange.aStart.Tab(); 1088 OSL_ENSURE( rRange.aEnd.Tab() == nTab, "GetPrintArea: Tab differ" ); 1089 1090 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1091 1092 bool bAny = false; 1093 long nEndX = 0; 1094 long nEndY = 0; 1095 long nStartX = LONG_MAX; 1096 long nStartY = LONG_MAX; 1097 1098 // Calculate borders 1099 1100 if (!bSetHor) 1101 { 1102 nStartX = 0; 1103 SCCOL nStartCol = rRange.aStart.Col(); 1104 SCCOL i; 1105 for (i=0; i<nStartCol; i++) 1106 nStartX +=pDoc->GetColWidth(i,nTab); 1107 nEndX = nStartX; 1108 SCCOL nEndCol = rRange.aEnd.Col(); 1109 for (i=nStartCol; i<=nEndCol; i++) 1110 nEndX += pDoc->GetColWidth(i,nTab); 1111 nStartX = TwipsToHmm( nStartX ); 1112 nEndX = TwipsToHmm( nEndX ); 1113 } 1114 if (!bSetVer) 1115 { 1116 nStartY = pDoc->GetRowHeight( 0, rRange.aStart.Row()-1, nTab); 1117 nEndY = nStartY + pDoc->GetRowHeight( rRange.aStart.Row(), 1118 rRange.aEnd.Row(), nTab); 1119 nStartY = TwipsToHmm( nStartY ); 1120 nEndY = TwipsToHmm( nEndY ); 1121 } 1122 1123 if ( bNegativePage ) 1124 { 1125 nStartX = -nStartX; // positions are negative, swap start/end so the same comparisons work 1126 nEndX = -nEndX; 1127 ::std::swap( nStartX, nEndX ); 1128 } 1129 1130 const SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1131 OSL_ENSURE(pPage,"Page not found"); 1132 if (pPage) 1133 { 1134 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1135 SdrObject* pObject = aIter.Next(); 1136 while (pObject) 1137 { 1138 //TODO: test Flags (hidden?) 1139 1140 tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); 1141 bool bFit = true; 1142 if ( !bSetHor && ( aObjRect.Right() < nStartX || aObjRect.Left() > nEndX ) ) 1143 bFit = false; 1144 if ( !bSetVer && ( aObjRect.Bottom() < nStartY || aObjRect.Top() > nEndY ) ) 1145 bFit = false; 1146 // #i104716# don't include hidden note objects 1147 if ( bFit && pObject->GetLayer() != SC_LAYER_HIDDEN ) 1148 { 1149 if (bSetHor) 1150 { 1151 if (aObjRect.Left() < nStartX) nStartX = aObjRect.Left(); 1152 if (aObjRect.Right() > nEndX) nEndX = aObjRect.Right(); 1153 } 1154 if (bSetVer) 1155 { 1156 if (aObjRect.Top() < nStartY) nStartY = aObjRect.Top(); 1157 if (aObjRect.Bottom() > nEndY) nEndY = aObjRect.Bottom(); 1158 } 1159 bAny = true; 1160 } 1161 1162 pObject = aIter.Next(); 1163 } 1164 } 1165 1166 if ( bNegativePage ) 1167 { 1168 nStartX = -nStartX; // reverse transformation, so the same cell address calculation works 1169 nEndX = -nEndX; 1170 ::std::swap( nStartX, nEndX ); 1171 } 1172 1173 if (bAny) 1174 { 1175 OSL_ENSURE( nStartX<=nEndX && nStartY<=nEndY, "Start/End wrong in ScDrawLayer::GetPrintArea" ); 1176 1177 if (bSetHor) 1178 { 1179 nStartX = HmmToTwips( nStartX ); 1180 nEndX = HmmToTwips( nEndX ); 1181 long nWidth; 1182 1183 nWidth = 0; 1184 rRange.aStart.SetCol( 0 ); 1185 if (nWidth <= nStartX) 1186 { 1187 for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, MAXCOL)) 1188 { 1189 nWidth += pDoc->GetColWidth(nCol,nTab); 1190 if (nWidth > nStartX) 1191 { 1192 rRange.aStart.SetCol( nCol ); 1193 break; 1194 } 1195 } 1196 } 1197 1198 nWidth = 0; 1199 rRange.aEnd.SetCol( 0 ); 1200 if (nWidth <= nEndX) 1201 { 1202 for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, MAXCOL)) //TODO: start at Start 1203 { 1204 nWidth += pDoc->GetColWidth(nCol,nTab); 1205 if (nWidth > nEndX) 1206 { 1207 rRange.aEnd.SetCol( nCol ); 1208 break; 1209 } 1210 } 1211 } 1212 } 1213 1214 if (bSetVer) 1215 { 1216 nStartY = HmmToTwips( nStartY ); 1217 nEndY = HmmToTwips( nEndY ); 1218 SCROW nRow = pDoc->GetRowForHeight( nTab, nStartY); 1219 rRange.aStart.SetRow( nRow>0 ? (nRow-1) : 0); 1220 nRow = pDoc->GetRowForHeight( nTab, nEndY); 1221 rRange.aEnd.SetRow( nRow == MAXROW ? MAXROW : 1222 (nRow>0 ? (nRow-1) : 0)); 1223 } 1224 } 1225 else 1226 { 1227 if (bSetHor) 1228 { 1229 rRange.aStart.SetCol(0); 1230 rRange.aEnd.SetCol(0); 1231 } 1232 if (bSetVer) 1233 { 1234 rRange.aStart.SetRow(0); 1235 rRange.aEnd.SetRow(0); 1236 } 1237 } 1238 return bAny; 1239 } 1240 1241 void ScDrawLayer::AddCalcUndo( std::unique_ptr<SdrUndoAction> pUndo ) 1242 { 1243 if (bRecording) 1244 { 1245 if (!pUndoGroup) 1246 pUndoGroup.reset(new SdrUndoGroup(*this)); 1247 1248 pUndoGroup->AddAction( std::move(pUndo) ); 1249 } 1250 } 1251 1252 void ScDrawLayer::BeginCalcUndo(bool bDisableTextEditUsesCommonUndoManager) 1253 { 1254 SetDisableTextEditUsesCommonUndoManager(bDisableTextEditUsesCommonUndoManager); 1255 pUndoGroup.reset(); 1256 bRecording = true; 1257 } 1258 1259 std::unique_ptr<SdrUndoGroup> ScDrawLayer::GetCalcUndo() 1260 { 1261 std::unique_ptr<SdrUndoGroup> pRet = std::move(pUndoGroup); 1262 bRecording = false; 1263 SetDisableTextEditUsesCommonUndoManager(false); 1264 return pRet; 1265 } 1266 1267 void ScDrawLayer::MoveArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, 1268 SCCOL nDx,SCROW nDy, bool bInsDel, bool bUpdateNoteCaptionPos ) 1269 { 1270 OSL_ENSURE( pDoc, "ScDrawLayer::MoveArea without document" ); 1271 if ( !pDoc ) 1272 return; 1273 1274 if (!bAdjustEnabled) 1275 return; 1276 1277 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1278 1279 tools::Rectangle aRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); 1280 lcl_ReverseTwipsToMM( aRect ); 1281 //TODO: use twips directly? 1282 1283 Point aMove; 1284 1285 if (nDx > 0) 1286 for (SCCOL s=0; s<nDx; s++) 1287 aMove.AdjustX(pDoc->GetColWidth(s+nCol1,nTab) ); 1288 else 1289 for (SCCOL s=-1; s>=nDx; s--) 1290 aMove.AdjustX( -(pDoc->GetColWidth(s+nCol1,nTab)) ); 1291 if (nDy > 0) 1292 aMove.AdjustY(pDoc->GetRowHeight( nRow1, nRow1+nDy-1, nTab) ); 1293 else 1294 aMove.AdjustY( -sal_Int16(pDoc->GetRowHeight( nRow1+nDy, nRow1-1, nTab)) ); 1295 1296 if ( bNegativePage ) 1297 aMove.setX( -aMove.X() ); 1298 1299 Point aTopLeft = aRect.TopLeft(); // Beginning when zoomed out 1300 if (bInsDel) 1301 { 1302 if ( aMove.X() != 0 && nDx < 0 ) // nDx counts cells, sign is independent of RTL 1303 aTopLeft.AdjustX(aMove.X() ); 1304 if ( aMove.Y() < 0 ) 1305 aTopLeft.AdjustY(aMove.Y() ); 1306 } 1307 1308 // Detectiv arrows: Adjust cell position 1309 1310 MoveCells( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy, bUpdateNoteCaptionPos ); 1311 } 1312 1313 bool ScDrawLayer::HasObjectsInRows( SCTAB nTab, SCROW nStartRow, SCROW nEndRow ) 1314 { 1315 OSL_ENSURE( pDoc, "ScDrawLayer::HasObjectsInRows without document" ); 1316 if ( !pDoc ) 1317 return false; 1318 1319 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1320 OSL_ENSURE(pPage,"Page not found"); 1321 if (!pPage) 1322 return false; 1323 1324 // for an empty page, there's no need to calculate the row heights 1325 if (!pPage->GetObjCount()) 1326 return false; 1327 1328 tools::Rectangle aTestRect; 1329 1330 aTestRect.AdjustTop(pDoc->GetRowHeight( 0, nStartRow-1, nTab) ); 1331 1332 if (nEndRow==MAXROW) 1333 aTestRect.SetBottom( MAXMM ); 1334 else 1335 { 1336 aTestRect.SetBottom( aTestRect.Top() ); 1337 aTestRect.AdjustBottom(pDoc->GetRowHeight( nStartRow, nEndRow, nTab) ); 1338 aTestRect.SetBottom(TwipsToHmm( aTestRect.Bottom() )); 1339 } 1340 1341 aTestRect.SetTop(TwipsToHmm( aTestRect.Top() )); 1342 1343 aTestRect.SetLeft( 0 ); 1344 aTestRect.SetRight( MAXMM ); 1345 1346 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1347 if ( bNegativePage ) 1348 MirrorRectRTL( aTestRect ); 1349 1350 bool bFound = false; 1351 1352 tools::Rectangle aObjRect; 1353 SdrObjListIter aIter( pPage ); 1354 SdrObject* pObject = aIter.Next(); 1355 while ( pObject && !bFound ) 1356 { 1357 aObjRect = pObject->GetSnapRect(); //TODO: GetLogicRect ? 1358 if (aTestRect.IsInside(aObjRect.TopLeft()) || aTestRect.IsInside(aObjRect.BottomLeft())) 1359 bFound = true; 1360 1361 pObject = aIter.Next(); 1362 } 1363 1364 return bFound; 1365 } 1366 1367 void ScDrawLayer::DeleteObjectsInArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, 1368 SCCOL nCol2,SCROW nRow2, bool bAnchored ) 1369 { 1370 OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInArea without document" ); 1371 if ( !pDoc ) 1372 return; 1373 1374 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1375 OSL_ENSURE(pPage,"Page ?"); 1376 if (!pPage) 1377 return; 1378 1379 pPage->RecalcObjOrdNums(); 1380 1381 const size_t nObjCount = pPage->GetObjCount(); 1382 if (nObjCount) 1383 { 1384 size_t nDelCount = 0; 1385 tools::Rectangle aDelRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); 1386 1387 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]); 1388 1389 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1390 SdrObject* pObject = aIter.Next(); 1391 while (pObject) 1392 { 1393 // do not delete note caption, they are always handled by the cell note 1394 // TODO: detective objects are still deleted, is this desired? 1395 if (!IsNoteCaption( pObject )) 1396 { 1397 tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); 1398 if (aDelRect.IsInside(aObjRect)) 1399 { 1400 if (bAnchored) 1401 { 1402 ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject); 1403 if(aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE) 1404 ppObj[nDelCount++] = pObject; 1405 } 1406 else 1407 ppObj[nDelCount++] = pObject; 1408 } 1409 } 1410 1411 pObject = aIter.Next(); 1412 } 1413 1414 if (bRecording) 1415 for (size_t i=1; i<=nDelCount; ++i) 1416 AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) ); 1417 1418 for (size_t i=1; i<=nDelCount; ++i) 1419 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); 1420 } 1421 } 1422 1423 void ScDrawLayer::DeleteObjectsInSelection( const ScMarkData& rMark ) 1424 { 1425 OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInSelection without document" ); 1426 if ( !pDoc ) 1427 return; 1428 1429 if ( !rMark.IsMultiMarked() ) 1430 return; 1431 1432 ScRange aMarkRange; 1433 rMark.GetMultiMarkArea( aMarkRange ); 1434 1435 SCTAB nTabCount = pDoc->GetTableCount(); 1436 for (const SCTAB nTab : rMark) 1437 { 1438 if (nTab >= nTabCount) 1439 break; 1440 1441 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1442 if (pPage) 1443 { 1444 pPage->RecalcObjOrdNums(); 1445 const size_t nObjCount = pPage->GetObjCount(); 1446 if (nObjCount) 1447 { 1448 size_t nDelCount = 0; 1449 // Rectangle around the whole selection 1450 tools::Rectangle aMarkBound = pDoc->GetMMRect( 1451 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), 1452 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab ); 1453 1454 std::unique_ptr<SdrObject*[]> ppObj(new SdrObject*[nObjCount]); 1455 1456 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1457 SdrObject* pObject = aIter.Next(); 1458 while (pObject) 1459 { 1460 // do not delete note caption, they are always handled by the cell note 1461 // TODO: detective objects are still deleted, is this desired? 1462 if (!IsNoteCaption( pObject )) 1463 { 1464 tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); 1465 ScRange aRange = pDoc->GetRange(nTab, aObjRect); 1466 bool bObjectInMarkArea = 1467 aMarkBound.IsInside(aObjRect) && rMark.IsAllMarked(aRange); 1468 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject); 1469 ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject); 1470 bool bObjectAnchoredToMarkedCell 1471 = ((aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE) 1472 && pObjData && rMark.IsCellMarked(pObjData->maStart.Col(), 1473 pObjData->maStart.Row())); 1474 if (bObjectInMarkArea || bObjectAnchoredToMarkedCell) 1475 { 1476 ppObj[nDelCount++] = pObject; 1477 } 1478 } 1479 1480 pObject = aIter.Next(); 1481 } 1482 1483 // Delete objects (backwards) 1484 1485 if (bRecording) 1486 for (size_t i=1; i<=nDelCount; ++i) 1487 AddCalcUndo( std::make_unique<SdrUndoRemoveObj>( *ppObj[nDelCount-i] ) ); 1488 1489 for (size_t i=1; i<=nDelCount; ++i) 1490 pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); 1491 } 1492 } 1493 else 1494 { 1495 OSL_FAIL("pPage?"); 1496 } 1497 } 1498 } 1499 1500 void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rectangle& rRange ) 1501 { 1502 // copy everything in the specified range into the same page (sheet) in the clipboard doc 1503 1504 SdrPage* pSrcPage = GetPage(static_cast<sal_uInt16>(nTab)); 1505 if (pSrcPage) 1506 { 1507 ScDrawLayer* pDestModel = nullptr; 1508 SdrPage* pDestPage = nullptr; 1509 1510 SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); 1511 SdrObject* pOldObject = aIter.Next(); 1512 while (pOldObject) 1513 { 1514 tools::Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); 1515 1516 bool bObjectInArea = rRange.IsInside(aObjRect); 1517 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); 1518 if (pObjData) 1519 { 1520 ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nTab); 1521 bObjectInArea = bObjectInArea || aClipRange.In(pObjData->maStart); 1522 } 1523 1524 // do not copy internal objects (detective) and note captions 1525 if (bObjectInArea && pOldObject->GetLayer() != SC_LAYER_INTERN 1526 && !IsNoteCaption(pOldObject)) 1527 { 1528 if ( !pDestModel ) 1529 { 1530 pDestModel = pClipDoc->GetDrawLayer(); // does the document already have a drawing layer? 1531 if ( !pDestModel ) 1532 { 1533 // allocate drawing layer in clipboard document only if there are objects to copy 1534 1535 pClipDoc->InitDrawLayer(); //TODO: create contiguous pages 1536 pDestModel = pClipDoc->GetDrawLayer(); 1537 } 1538 if (pDestModel) 1539 pDestPage = pDestModel->GetPage( static_cast<sal_uInt16>(nTab) ); 1540 } 1541 1542 OSL_ENSURE( pDestPage, "no page" ); 1543 if (pDestPage) 1544 { 1545 // Clone to target SdrModel 1546 SdrObject* pNewObject(pOldObject->CloneSdrObject(*pDestModel)); 1547 1548 uno::Reference< chart2::XChartDocument > xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject ) ); 1549 if(!xOldChart.is())//#i110034# do not move charts as they lose all their data references otherwise 1550 pNewObject->NbcMove(Size(0,0)); 1551 pDestPage->InsertObject( pNewObject ); 1552 1553 // no undo needed in clipboard document 1554 // charts are not updated 1555 } 1556 } 1557 1558 pOldObject = aIter.Next(); 1559 } 1560 } 1561 } 1562 1563 static bool lcl_IsAllInRange( const ::std::vector< ScRangeList >& rRangesVector, const ScRange& rClipRange ) 1564 { 1565 // check if every range of rRangesVector is completely in rClipRange 1566 1567 for( const ScRangeList& rRanges : rRangesVector ) 1568 { 1569 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) 1570 { 1571 const ScRange & rRange = rRanges[ i ]; 1572 if ( !rClipRange.In( rRange ) ) 1573 { 1574 return false; // at least one range is not valid 1575 } 1576 } 1577 } 1578 1579 return true; // everything is fine 1580 } 1581 1582 static bool lcl_MoveRanges( ::std::vector< ScRangeList >& rRangesVector, const ScRange& rSourceRange, const ScAddress& rDestPos ) 1583 { 1584 bool bChanged = false; 1585 1586 ScRange aErrorRange( ScAddress::UNINITIALIZED ); 1587 for( ScRangeList& rRanges : rRangesVector ) 1588 { 1589 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) 1590 { 1591 ScRange & rRange = rRanges[ i ]; 1592 if ( rSourceRange.In( rRange ) ) 1593 { 1594 SCCOL nDiffX = rDestPos.Col() - rSourceRange.aStart.Col(); 1595 SCROW nDiffY = rDestPos.Row() - rSourceRange.aStart.Row(); 1596 SCTAB nDiffZ = rDestPos.Tab() - rSourceRange.aStart.Tab(); 1597 if (!rRange.Move( nDiffX, nDiffY, nDiffZ, aErrorRange)) 1598 { 1599 assert(!"can't move range"); 1600 } 1601 bChanged = true; 1602 } 1603 } 1604 } 1605 1606 return bChanged; 1607 } 1608 1609 void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const tools::Rectangle& rSourceRange, 1610 const ScAddress& rDestPos, const tools::Rectangle& rDestRange ) 1611 { 1612 OSL_ENSURE( pDoc, "ScDrawLayer::CopyFromClip without document" ); 1613 if ( !pDoc ) 1614 return; 1615 1616 if (!pClipModel) 1617 return; 1618 1619 if (bDrawIsInUndo) //TODO: can this happen? 1620 { 1621 OSL_FAIL("CopyFromClip, bDrawIsInUndo"); 1622 return; 1623 } 1624 1625 bool bMirrorObj = ( rSourceRange.Left() < 0 && rSourceRange.Right() < 0 && 1626 rDestRange.Left() > 0 && rDestRange.Right() > 0 ) || 1627 ( rSourceRange.Left() > 0 && rSourceRange.Right() > 0 && 1628 rDestRange.Left() < 0 && rDestRange.Right() < 0 ); 1629 tools::Rectangle aMirroredSource = rSourceRange; 1630 if ( bMirrorObj ) 1631 MirrorRectRTL( aMirroredSource ); 1632 1633 SCTAB nDestTab = rDestPos.Tab(); 1634 1635 SdrPage* pSrcPage = pClipModel->GetPage(static_cast<sal_uInt16>(nSourceTab)); 1636 SdrPage* pDestPage = GetPage(static_cast<sal_uInt16>(nDestTab)); 1637 OSL_ENSURE( pSrcPage && pDestPage, "draw page missing" ); 1638 if ( !pSrcPage || !pDestPage ) 1639 return; 1640 1641 SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); 1642 SdrObject* pOldObject = aIter.Next(); 1643 1644 ScDocument* pClipDoc = pClipModel->GetDocument(); 1645 // a clipboard document and its source share the same document item pool, 1646 // so the pointers can be compared to see if this is copy&paste within 1647 // the same document 1648 bool bSameDoc = pDoc && pClipDoc && pDoc->GetPool() == pClipDoc->GetPool(); 1649 bool bDestClip = pDoc && pDoc->IsClipboard(); 1650 1651 //#i110034# charts need correct sheet names for xml range conversion during load 1652 //so the target sheet name is temporarily renamed (if we have any SdrObjects) 1653 OUString aDestTabName; 1654 bool bRestoreDestTabName = false; 1655 if( pOldObject && !bSameDoc && !bDestClip ) 1656 { 1657 if( pDoc && pClipDoc ) 1658 { 1659 OUString aSourceTabName; 1660 if( pClipDoc->GetName( nSourceTab, aSourceTabName ) 1661 && pDoc->GetName( nDestTab, aDestTabName ) ) 1662 { 1663 if( aSourceTabName != aDestTabName && 1664 pDoc->ValidNewTabName(aSourceTabName) ) 1665 { 1666 bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName ); 1667 } 1668 } 1669 } 1670 } 1671 1672 // first mirror, then move 1673 Size aMove( rDestRange.Left() - aMirroredSource.Left(), rDestRange.Top() - aMirroredSource.Top() ); 1674 1675 long nDestWidth = rDestRange.GetWidth(); 1676 long nDestHeight = rDestRange.GetHeight(); 1677 long nSourceWidth = rSourceRange.GetWidth(); 1678 long nSourceHeight = rSourceRange.GetHeight(); 1679 1680 long nWidthDiff = nDestWidth - nSourceWidth; 1681 long nHeightDiff = nDestHeight - nSourceHeight; 1682 1683 Fraction aHorFract(1,1); 1684 Fraction aVerFract(1,1); 1685 bool bResize = false; 1686 // sizes can differ by 1 from twips->1/100mm conversion for equal cell sizes, 1687 // don't resize to empty size when pasting into hidden columns or rows 1688 if ( std::abs(nWidthDiff) > 1 && nDestWidth > 1 && nSourceWidth > 1 ) 1689 { 1690 aHorFract = Fraction( nDestWidth, nSourceWidth ); 1691 bResize = true; 1692 } 1693 if ( std::abs(nHeightDiff) > 1 && nDestHeight > 1 && nSourceHeight > 1 ) 1694 { 1695 aVerFract = Fraction( nDestHeight, nSourceHeight ); 1696 bResize = true; 1697 } 1698 Point aRefPos = rDestRange.TopLeft(); // for resizing (after moving) 1699 1700 while (pOldObject) 1701 { 1702 tools::Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); 1703 // do not copy internal objects (detective) and note captions 1704 1705 SCTAB nClipTab = bRestoreDestTabName ? nDestTab : nSourceTab; 1706 ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nClipTab); 1707 1708 bool bObjectInArea = rSourceRange.IsInside(aObjRect); 1709 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); 1710 if (pObjData) // Consider images anchored to the copied cell 1711 bObjectInArea = bObjectInArea || aClipRange.In(pObjData->maStart); 1712 if (bObjectInArea && (pOldObject->GetLayer() != SC_LAYER_INTERN) 1713 && !IsNoteCaption(pOldObject)) 1714 { 1715 // Clone to target SdrModel 1716 SdrObject* pNewObject(pOldObject->CloneSdrObject(*this)); 1717 1718 if ( bMirrorObj ) 1719 MirrorRTL( pNewObject ); // first mirror, then move 1720 1721 pNewObject->NbcMove( aMove ); 1722 if ( bResize ) 1723 pNewObject->NbcResize( aRefPos, aHorFract, aVerFract ); 1724 1725 pDestPage->InsertObject( pNewObject ); 1726 if (bRecording) 1727 AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) ); 1728 1729 //#i110034# handle chart data references (after InsertObject) 1730 1731 if ( pNewObject->GetObjIdentifier() == OBJ_OLE2 ) 1732 { 1733 uno::Reference< embed::XEmbeddedObject > xIPObj = static_cast<SdrOle2Obj*>(pNewObject)->GetObjRef(); 1734 uno::Reference< embed::XClassifiedObject > xClassified( xIPObj, uno::UNO_QUERY ); 1735 SvGlobalName aObjectClassName; 1736 if ( xClassified.is() ) 1737 { 1738 try { 1739 aObjectClassName = SvGlobalName( xClassified->getClassID() ); 1740 } catch( uno::Exception& ) 1741 { 1742 // TODO: handle error? 1743 } 1744 } 1745 1746 if ( xIPObj.is() && SotExchange::IsChart( aObjectClassName ) ) 1747 { 1748 uno::Reference< chart2::XChartDocument > xNewChart( ScChartHelper::GetChartFromSdrObject( pNewObject ) ); 1749 if( xNewChart.is() && !xNewChart->hasInternalDataProvider() ) 1750 { 1751 OUString aChartName = static_cast<SdrOle2Obj*>(pNewObject)->GetPersistName(); 1752 ::std::vector< ScRangeList > aRangesVector; 1753 pDoc->GetChartRanges( aChartName, aRangesVector, pDoc ); 1754 if( !aRangesVector.empty() ) 1755 { 1756 bool bInSourceRange = false; 1757 if ( pClipDoc ) 1758 { 1759 bInSourceRange = lcl_IsAllInRange( aRangesVector, aClipRange ); 1760 } 1761 1762 // always lose references when pasting into a clipboard document (transpose) 1763 if ( ( bInSourceRange || bSameDoc ) && !bDestClip ) 1764 { 1765 if ( bInSourceRange ) 1766 { 1767 if ( rDestPos != aClipRange.aStart ) 1768 { 1769 // update the data ranges to the new (copied) position 1770 if ( lcl_MoveRanges( aRangesVector, aClipRange, rDestPos ) ) 1771 pDoc->SetChartRanges( aChartName, aRangesVector ); 1772 } 1773 } 1774 else 1775 { 1776 // leave the ranges unchanged 1777 } 1778 } 1779 else 1780 { 1781 // pasting into a new document without the complete source data 1782 // -> break connection to source data and switch to own data 1783 1784 uno::Reference< chart::XChartDocument > xOldChartDoc( ScChartHelper::GetChartFromSdrObject( pOldObject ), uno::UNO_QUERY ); 1785 uno::Reference< chart::XChartDocument > xNewChartDoc( xNewChart, uno::UNO_QUERY ); 1786 if( xOldChartDoc.is() && xNewChartDoc.is() ) 1787 xNewChartDoc->attachData( xOldChartDoc->getData() ); 1788 1789 // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc) 1790 } 1791 } 1792 } 1793 } 1794 } 1795 } 1796 1797 pOldObject = aIter.Next(); 1798 } 1799 1800 if( bRestoreDestTabName ) 1801 pDoc->RenameTab( nDestTab, aDestTabName ); 1802 } 1803 1804 void ScDrawLayer::MirrorRTL( SdrObject* pObj ) 1805 { 1806 sal_uInt16 nIdent = pObj->GetObjIdentifier(); 1807 1808 // don't mirror OLE or graphics, otherwise ask the object 1809 // if it can be mirrored 1810 bool bCanMirror = ( nIdent != OBJ_GRAF && nIdent != OBJ_OLE2 ); 1811 if (bCanMirror) 1812 { 1813 SdrObjTransformInfoRec aInfo; 1814 pObj->TakeObjInfo( aInfo ); 1815 bCanMirror = aInfo.bMirror90Allowed; 1816 } 1817 1818 if (bCanMirror) 1819 { 1820 Point aRef1( 0, 0 ); 1821 Point aRef2( 0, 1 ); 1822 if (bRecording) 1823 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1824 pObj->Mirror( aRef1, aRef2 ); 1825 } 1826 else 1827 { 1828 // Move instead of mirroring: 1829 // New start position is negative of old end position 1830 // -> move by sum of start and end position 1831 tools::Rectangle aObjRect = pObj->GetLogicRect(); 1832 Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); 1833 if (bRecording) 1834 AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) ); 1835 pObj->Move( aMoveSize ); 1836 } 1837 } 1838 1839 void ScDrawLayer::MirrorRectRTL( tools::Rectangle& rRect ) 1840 { 1841 // mirror and swap left/right 1842 long nTemp = rRect.Left(); 1843 rRect.SetLeft( -rRect.Right() ); 1844 rRect.SetRight( -nTemp ); 1845 } 1846 1847 tools::Rectangle ScDrawLayer::GetCellRect( const ScDocument& rDoc, const ScAddress& rPos, bool bMergedCell ) 1848 { 1849 tools::Rectangle aCellRect; 1850 OSL_ENSURE( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ), "ScDrawLayer::GetCellRect - invalid cell address" ); 1851 if( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ) ) 1852 { 1853 // find top left position of passed cell address 1854 Point aTopLeft; 1855 for( SCCOL nCol = 0; nCol < rPos.Col(); ++nCol ) 1856 aTopLeft.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) ); 1857 if( rPos.Row() > 0 ) 1858 aTopLeft.AdjustY(rDoc.GetRowHeight( 0, rPos.Row() - 1, rPos.Tab() ) ); 1859 1860 // find bottom-right position of passed cell address 1861 ScAddress aEndPos = rPos; 1862 if( bMergedCell ) 1863 { 1864 const ScMergeAttr* pMerge = rDoc.GetAttr( rPos, ATTR_MERGE ); 1865 if( pMerge->GetColMerge() > 1 ) 1866 aEndPos.IncCol( pMerge->GetColMerge() - 1 ); 1867 if( pMerge->GetRowMerge() > 1 ) 1868 aEndPos.IncRow( pMerge->GetRowMerge() - 1 ); 1869 } 1870 Point aBotRight = aTopLeft; 1871 for( SCCOL nCol = rPos.Col(); nCol <= aEndPos.Col(); ++nCol ) 1872 aBotRight.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) ); 1873 aBotRight.AdjustY(rDoc.GetRowHeight( rPos.Row(), aEndPos.Row(), rPos.Tab() ) ); 1874 1875 // twips -> 1/100 mm 1876 aTopLeft.setX( static_cast< long >( aTopLeft.X() * HMM_PER_TWIPS ) ); 1877 aTopLeft.setY( static_cast< long >( aTopLeft.Y() * HMM_PER_TWIPS ) ); 1878 aBotRight.setX( static_cast< long >( aBotRight.X() * HMM_PER_TWIPS ) ); 1879 aBotRight.setY( static_cast< long >( aBotRight.Y() * HMM_PER_TWIPS ) ); 1880 1881 aCellRect = tools::Rectangle( aTopLeft, aBotRight ); 1882 if( rDoc.IsNegativePage( rPos.Tab() ) ) 1883 MirrorRectRTL( aCellRect ); 1884 } 1885 return aCellRect; 1886 } 1887 1888 OUString ScDrawLayer::GetVisibleName( const SdrObject* pObj ) 1889 { 1890 OUString aName = pObj->GetName(); 1891 if ( pObj->GetObjIdentifier() == OBJ_OLE2 ) 1892 { 1893 // For OLE, the user defined name (GetName) is used 1894 // if it's not empty (accepting possibly duplicate names), 1895 // otherwise the persist name is used so every object appears 1896 // in the Navigator at all. 1897 1898 if ( aName.isEmpty() ) 1899 aName = static_cast<const SdrOle2Obj*>(pObj)->GetPersistName(); 1900 } 1901 return aName; 1902 } 1903 1904 static bool IsNamedObject( const SdrObject* pObj, const OUString& rName ) 1905 { 1906 // sal_True if rName is the object's Name or PersistName 1907 // (used to find a named object) 1908 1909 return ( pObj->GetName() == rName || 1910 ( pObj->GetObjIdentifier() == OBJ_OLE2 && 1911 static_cast<const SdrOle2Obj*>(pObj)->GetPersistName() == rName ) ); 1912 } 1913 1914 SdrObject* ScDrawLayer::GetNamedObject( const OUString& rName, sal_uInt16 nId, SCTAB& rFoundTab ) const 1915 { 1916 sal_uInt16 nTabCount = GetPageCount(); 1917 for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) 1918 { 1919 const SdrPage* pPage = GetPage(nTab); 1920 OSL_ENSURE(pPage,"Page ?"); 1921 if (pPage) 1922 { 1923 SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); 1924 SdrObject* pObject = aIter.Next(); 1925 while (pObject) 1926 { 1927 if ( nId == 0 || pObject->GetObjIdentifier() == nId ) 1928 if ( IsNamedObject( pObject, rName ) ) 1929 { 1930 rFoundTab = static_cast<SCTAB>(nTab); 1931 return pObject; 1932 } 1933 1934 pObject = aIter.Next(); 1935 } 1936 } 1937 } 1938 1939 return nullptr; 1940 } 1941 1942 OUString ScDrawLayer::GetNewGraphicName( long* pnCounter ) const 1943 { 1944 OUString aBase = ScResId(STR_GRAPHICNAME) + " "; 1945 1946 bool bThere = true; 1947 OUString aGraphicName; 1948 SCTAB nDummy; 1949 long nId = pnCounter ? *pnCounter : 0; 1950 while (bThere) 1951 { 1952 ++nId; 1953 aGraphicName = aBase + OUString::number( nId ); 1954 bThere = ( GetNamedObject( aGraphicName, 0, nDummy ) != nullptr ); 1955 } 1956 1957 if ( pnCounter ) 1958 *pnCounter = nId; 1959 1960 return aGraphicName; 1961 } 1962 1963 void ScDrawLayer::EnsureGraphicNames() 1964 { 1965 // make sure all graphic objects have names (after Excel import etc.) 1966 1967 sal_uInt16 nTabCount = GetPageCount(); 1968 for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) 1969 { 1970 SdrPage* pPage = GetPage(nTab); 1971 OSL_ENSURE(pPage,"Page ?"); 1972 if (pPage) 1973 { 1974 SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); 1975 SdrObject* pObject = aIter.Next(); 1976 1977 /* The index passed to GetNewGraphicName() will be set to 1978 the used index in each call. This prevents the repeated search 1979 for all names from 1 to current index. */ 1980 long nCounter = 0; 1981 1982 while (pObject) 1983 { 1984 if ( pObject->GetObjIdentifier() == OBJ_GRAF && pObject->GetName().isEmpty()) 1985 pObject->SetName( GetNewGraphicName( &nCounter ) ); 1986 1987 pObject = aIter.Next(); 1988 } 1989 } 1990 } 1991 } 1992 1993 namespace 1994 { 1995 SdrObjUserData* GetFirstUserDataOfType(const SdrObject *pObj, sal_uInt16 nId) 1996 { 1997 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 1998 for( sal_uInt16 i = 0; i < nCount; i++ ) 1999 { 2000 SdrObjUserData* pData = pObj->GetUserData( i ); 2001 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId ) 2002 return pData; 2003 } 2004 return nullptr; 2005 } 2006 2007 void DeleteFirstUserDataOfType(SdrObject *pObj, sal_uInt16 nId) 2008 { 2009 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 2010 for( sal_uInt16 i = nCount; i > 0; i-- ) 2011 { 2012 SdrObjUserData* pData = pObj->GetUserData( i-1 ); 2013 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId ) 2014 pObj->DeleteUserData(i-1); 2015 } 2016 } 2017 } 2018 2019 void ScDrawLayer::SetVisualCellAnchored( SdrObject &rObj, const ScDrawObjData &rAnchor ) 2020 { 2021 ScDrawObjData* pAnchor = GetNonRotatedObjData( &rObj, true ); 2022 pAnchor->maStart = rAnchor.maStart; 2023 pAnchor->maEnd = rAnchor.maEnd; 2024 pAnchor->maStartOffset = rAnchor.maStartOffset; 2025 pAnchor->maEndOffset = rAnchor.maEndOffset; 2026 pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell; 2027 } 2028 2029 void ScDrawLayer::SetCellAnchored( SdrObject &rObj, const ScDrawObjData &rAnchor ) 2030 { 2031 ScDrawObjData* pAnchor = GetObjData( &rObj, true ); 2032 pAnchor->maStart = rAnchor.maStart; 2033 pAnchor->maEnd = rAnchor.maEnd; 2034 pAnchor->maStartOffset = rAnchor.maStartOffset; 2035 pAnchor->maEndOffset = rAnchor.maEndOffset; 2036 pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell; 2037 } 2038 2039 void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject &rObj, const ScDocument &rDoc, SCTAB nTab, 2040 bool bResizeWithCell ) 2041 { 2042 ScDrawObjData aAnchor; 2043 // set anchor in terms of the visual ( SnapRect ) 2044 // object ( e.g. for when object is rotated ) 2045 const tools::Rectangle aObjRect(rObj.GetSnapRect()); 2046 GetCellAnchorFromPosition( 2047 aObjRect, 2048 aAnchor, 2049 rDoc, 2050 nTab); 2051 2052 aAnchor.mbResizeWithCell = bResizeWithCell; 2053 SetCellAnchored( rObj, aAnchor ); 2054 // - keep also an anchor in terms of the Logic ( untransformed ) object 2055 // because that's what we stored ( and still do ) to xml 2056 ScDrawObjData aVisAnchor; 2057 const tools::Rectangle aObjRect2(rObj.GetLogicRect()); 2058 GetCellAnchorFromPosition( 2059 aObjRect2, 2060 aVisAnchor, 2061 rDoc, 2062 nTab); 2063 2064 aVisAnchor.mbResizeWithCell = bResizeWithCell; 2065 SetVisualCellAnchored( rObj, aVisAnchor ); 2066 // absolutely necessary to set flag that in order to prevent ScDrawLayer::RecalcPos 2067 // doing an initialisation hack 2068 if ( ScDrawObjData* pAnchor = GetObjData( &rObj ) ) 2069 { 2070 pAnchor->setShapeRect(&rDoc, rObj.GetSnapRect()); 2071 } 2072 } 2073 2074 void ScDrawLayer::GetCellAnchorFromPosition( 2075 const tools::Rectangle &rObjRect, 2076 ScDrawObjData &rAnchor, 2077 const ScDocument &rDoc, 2078 SCTAB nTab, 2079 bool bHiddenAsZero) 2080 { 2081 ScRange aRange = rDoc.GetRange( nTab, rObjRect, bHiddenAsZero ); 2082 2083 tools::Rectangle aCellRect; 2084 2085 rAnchor.maStart = aRange.aStart; 2086 aCellRect = rDoc.GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), 2087 aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab(), bHiddenAsZero ); 2088 rAnchor.maStartOffset.setY( rObjRect.Top()-aCellRect.Top() ); 2089 if (!rDoc.IsNegativePage(nTab)) 2090 rAnchor.maStartOffset.setX( rObjRect.Left()-aCellRect.Left() ); 2091 else 2092 rAnchor.maStartOffset.setX( aCellRect.Right()-rObjRect.Right() ); 2093 2094 rAnchor.maEnd = aRange.aEnd; 2095 aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(), 2096 aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab(), bHiddenAsZero ); 2097 if (!rObjRect.IsEmpty()) 2098 rAnchor.maEndOffset.setY( rObjRect.Bottom()-aCellRect.Top() ); 2099 if (!rDoc.IsNegativePage(nTab)) 2100 { 2101 if (!rObjRect.IsEmpty()) 2102 rAnchor.maEndOffset.setX( rObjRect.Right()-aCellRect.Left() ); 2103 } 2104 else 2105 rAnchor.maEndOffset.setX( aCellRect.Right()-rObjRect.Left() ); 2106 } 2107 2108 void ScDrawLayer::UpdateCellAnchorFromPositionEnd( const SdrObject &rObj, ScDrawObjData &rAnchor, const ScDocument &rDoc, SCTAB nTab, bool bUseLogicRect ) 2109 { 2110 tools::Rectangle aObjRect(bUseLogicRect ? rObj.GetLogicRect() : rObj.GetSnapRect()); 2111 ScRange aRange = rDoc.GetRange( nTab, aObjRect ); 2112 2113 ScDrawObjData* pAnchor = &rAnchor; 2114 pAnchor->maEnd = aRange.aEnd; 2115 2116 tools::Rectangle aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(), 2117 aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab() ); 2118 pAnchor->maEndOffset.setY( aObjRect.Bottom()-aCellRect.Top() ); 2119 if (!rDoc.IsNegativePage(nTab)) 2120 pAnchor->maEndOffset.setX( aObjRect.Right()-aCellRect.Left() ); 2121 else 2122 pAnchor->maEndOffset.setX( aCellRect.Right()-aObjRect.Left() ); 2123 } 2124 2125 bool ScDrawLayer::IsCellAnchored( const SdrObject& rObj ) 2126 { 2127 // Cell anchored object always has a user data, to store the anchor cell 2128 // info. If it doesn't then it's page-anchored. 2129 return GetFirstUserDataOfType(&rObj, SC_UD_OBJDATA) != nullptr; 2130 } 2131 2132 bool ScDrawLayer::IsResizeWithCell( const SdrObject& rObj ) 2133 { 2134 // Cell anchored object always has a user data, to store the anchor cell 2135 // info. If it doesn't then it's page-anchored. 2136 ScDrawObjData* pDrawObjData = GetObjData(const_cast<SdrObject*>(&rObj)); 2137 if (!pDrawObjData) 2138 return false; 2139 2140 return pDrawObjData->mbResizeWithCell; 2141 } 2142 2143 void ScDrawLayer::SetPageAnchored( SdrObject &rObj ) 2144 { 2145 DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA); 2146 DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA); 2147 } 2148 2149 ScAnchorType ScDrawLayer::GetAnchorType( const SdrObject &rObj ) 2150 { 2151 //If this object has a cell anchor associated with it 2152 //then it's cell-anchored, otherwise it's page-anchored 2153 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(const_cast<SdrObject*>(&rObj)); 2154 2155 // When there is no cell anchor, it is page anchored. 2156 if (!pObjData) 2157 return SCA_PAGE; 2158 2159 // It's cell-anchored, check if the object resizes with the cell 2160 if (pObjData->mbResizeWithCell) 2161 return SCA_CELL_RESIZE; 2162 2163 return SCA_CELL; 2164 } 2165 2166 std::vector<SdrObject*> 2167 ScDrawLayer::GetObjectsAnchoredToRows(SCTAB nTab, SCROW nStartRow, SCROW nEndRow) 2168 { 2169 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 2170 if (!pPage || pPage->GetObjCount() < 1) 2171 return std::vector<SdrObject*>(); 2172 2173 std::vector<SdrObject*> aObjects; 2174 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2175 SdrObject* pObject = aIter.Next(); 2176 ScRange aRange( 0, nStartRow, nTab, MAXCOL, nEndRow, nTab); 2177 while (pObject) 2178 { 2179 if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently 2180 { 2181 ScDrawObjData* pObjData = GetObjData(pObject); 2182 if (pObjData && aRange.In(pObjData->maStart)) 2183 aObjects.push_back(pObject); 2184 } 2185 pObject = aIter.Next(); 2186 } 2187 return aObjects; 2188 } 2189 2190 std::map<SCROW, std::vector<SdrObject*>> 2191 ScDrawLayer::GetObjectsAnchoredToRange(SCTAB nTab, SCCOL nCol, SCROW nStartRow, SCROW nEndRow) 2192 { 2193 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 2194 if (!pPage || pPage->GetObjCount() < 1) 2195 return std::map<SCROW, std::vector<SdrObject*>>(); 2196 2197 std::map<SCROW, std::vector<SdrObject*>> aRowObjects; 2198 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2199 SdrObject* pObject = aIter.Next(); 2200 ScRange aRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab); 2201 while (pObject) 2202 { 2203 if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently 2204 { 2205 ScDrawObjData* pObjData = GetObjData(pObject); 2206 if (pObjData && aRange.In(pObjData->maStart)) 2207 aRowObjects[pObjData->maStart.Row()].push_back(pObject); 2208 } 2209 pObject = aIter.Next(); 2210 } 2211 return aRowObjects; 2212 } 2213 2214 bool ScDrawLayer::HasObjectsAnchoredInRange(const ScRange& rRange) 2215 { 2216 // This only works for one table at a time 2217 assert(rRange.aStart.Tab() == rRange.aEnd.Tab()); 2218 2219 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(rRange.aStart.Tab())); 2220 if (!pPage || pPage->GetObjCount() < 1) 2221 return false; 2222 2223 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2224 SdrObject* pObject = aIter.Next(); 2225 while (pObject) 2226 { 2227 if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently 2228 { 2229 ScDrawObjData* pObjData = GetObjData(pObject); 2230 if (pObjData && rRange.In(pObjData->maStart)) // Object is in given range 2231 return true; 2232 } 2233 pObject = aIter.Next(); 2234 } 2235 return false; 2236 } 2237 2238 void ScDrawLayer::MoveObject(SdrObject* pObject, const ScAddress& rNewPosition) 2239 { 2240 // Get anchor data 2241 ScDrawObjData* pObjData = GetObjData(pObject, false); 2242 if (!pObjData) 2243 return; 2244 const ScAddress aOldStart = pObjData->maStart; 2245 const ScAddress aOldEnd = pObjData->maEnd; 2246 2247 // Set start address 2248 pObjData->maStart = rNewPosition; 2249 2250 // Set end address 2251 const SCCOL nObjectColSpan = aOldEnd.Col() - aOldStart.Col(); 2252 const SCROW nObjectRowSpan = aOldEnd.Row() - aOldStart.Row(); 2253 ScAddress aNewEnd = rNewPosition; 2254 aNewEnd.IncRow(nObjectRowSpan); 2255 aNewEnd.IncCol(nObjectColSpan); 2256 pObjData->maEnd = aNewEnd; 2257 2258 // Update draw object according to new anchor 2259 RecalcPos(pObject, *pObjData, false, false); 2260 } 2261 2262 ScDrawObjData* ScDrawLayer::GetNonRotatedObjData( SdrObject* pObj, bool bCreate ) 2263 { 2264 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 2265 sal_uInt16 nFound = 0; 2266 for( sal_uInt16 i = 0; i < nCount; i++ ) 2267 { 2268 SdrObjUserData* pData = pObj->GetUserData( i ); 2269 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == SC_UD_OBJDATA && ++nFound == 2 ) 2270 return static_cast<ScDrawObjData*>(pData); 2271 } 2272 if( pObj && bCreate ) 2273 { 2274 ScDrawObjData* pData = new ScDrawObjData; 2275 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2276 return pData; 2277 } 2278 return nullptr; 2279 } 2280 2281 ScDrawObjData* ScDrawLayer::GetObjData( SdrObject* pObj, bool bCreate ) 2282 { 2283 if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_OBJDATA)) 2284 return static_cast<ScDrawObjData*>(pData); 2285 2286 if( pObj && bCreate ) 2287 { 2288 ScDrawObjData* pData = new ScDrawObjData; 2289 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2290 return pData; 2291 } 2292 return nullptr; 2293 } 2294 2295 ScDrawObjData* ScDrawLayer::GetObjDataTab( SdrObject* pObj, SCTAB nTab ) 2296 { 2297 ScDrawObjData* pData = GetObjData( pObj ); 2298 if ( pData ) 2299 { 2300 if ( pData->maStart.IsValid() ) 2301 pData->maStart.SetTab( nTab ); 2302 if ( pData->maEnd.IsValid() ) 2303 pData->maEnd.SetTab( nTab ); 2304 } 2305 return pData; 2306 } 2307 2308 bool ScDrawLayer::IsNoteCaption( SdrObject* pObj ) 2309 { 2310 ScDrawObjData* pData = pObj ? GetObjData( pObj ) : nullptr; 2311 return pData && pData->meType == ScDrawObjData::CellNote; 2312 } 2313 2314 ScDrawObjData* ScDrawLayer::GetNoteCaptionData( SdrObject* pObj, SCTAB nTab ) 2315 { 2316 ScDrawObjData* pData = pObj ? GetObjDataTab( pObj, nTab ) : nullptr; 2317 return (pData && pData->meType == ScDrawObjData::CellNote) ? pData : nullptr; 2318 } 2319 2320 ScMacroInfo* ScDrawLayer::GetMacroInfo( SdrObject* pObj, bool bCreate ) 2321 { 2322 if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_MACRODATA)) 2323 return static_cast<ScMacroInfo*>(pData); 2324 2325 if ( bCreate ) 2326 { 2327 ScMacroInfo* pData = new ScMacroInfo; 2328 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2329 return pData; 2330 } 2331 return nullptr; 2332 } 2333 2334 void ScDrawLayer::SetGlobalDrawPersist(SfxObjectShell* pPersist) 2335 { 2336 OSL_ENSURE(!pGlobalDrawPersist,"Multiple SetGlobalDrawPersist"); 2337 pGlobalDrawPersist = pPersist; 2338 } 2339 2340 void ScDrawLayer::SetChanged( bool bFlg /* = true */ ) 2341 { 2342 if ( bFlg && pDoc ) 2343 pDoc->SetChartListenerCollectionNeedsUpdate( true ); 2344 FmFormModel::SetChanged( bFlg ); 2345 } 2346 2347 css::uno::Reference< css::uno::XInterface > ScDrawLayer::createUnoModel() 2348 { 2349 css::uno::Reference< css::uno::XInterface > xRet; 2350 if( pDoc && pDoc->GetDocumentShell() ) 2351 xRet = pDoc->GetDocumentShell()->GetModel(); 2352 2353 return xRet; 2354 } 2355 2356 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2357
