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 #include <com/sun/star/embed/XEmbeddedObject.hpp> 25 26 #include <scitems.hxx> 27 #include <editeng/eeitem.hxx> 28 #include <editeng/fontitem.hxx> 29 #include <editeng/frmdiritem.hxx> 30 #include <sot/exchange.hxx> 31 #include <svx/objfac3d.hxx> 32 #include <svx/xtable.hxx> 33 #include <svx/svdoutl.hxx> 34 #include <svx/svditer.hxx> 35 #include <svx/svdlayer.hxx> 36 #include <svx/svdoashp.hxx> 37 #include <svx/svdobj.hxx> 38 #include <svx/svdocapt.hxx> 39 #include <svx/svdomeas.hxx> 40 #include <svx/svdoole2.hxx> 41 #include <svx/svdopath.hxx> 42 #include <svx/svdundo.hxx> 43 #include <svx/sdsxyitm.hxx> 44 #include <svx/svxids.hrc> 45 #include <svx/sxcecitm.hxx> 46 #include <svx/sdshitm.hxx> 47 #include <svx/sdtditm.hxx> 48 #include <svx/sdtagitm.hxx> 49 #include <svx/xflclit.hxx> 50 #include <svx/xfillit0.hxx> 51 #include <svx/xlineit0.hxx> 52 #include <svx/xlnstit.hxx> 53 #include <svx/xlnstwit.hxx> 54 #include <svx/xlnstcit.hxx> 55 #include <i18nlangtag/mslangid.hxx> 56 #include <editeng/unolingu.hxx> 57 #include <svx/drawitem.hxx> 58 #include <editeng/fhgtitem.hxx> 59 #include <editeng/scriptspaceitem.hxx> 60 #include <sfx2/objsh.hxx> 61 #include <svl/itempool.hxx> 62 #include <utility> 63 #include <vcl/canvastools.hxx> 64 #include <vcl/svapp.hxx> 65 #include <vcl/settings.hxx> 66 #include <tools/globname.hxx> 67 #include <tools/UnitConversion.hxx> 68 #include <osl/diagnose.h> 69 70 #include <basegfx/polygon/b2dpolygon.hxx> 71 #include <basegfx/polygon/b2dpolygontools.hxx> 72 73 #include <drwlayer.hxx> 74 #include <drawpage.hxx> 75 #include <global.hxx> 76 #include <document.hxx> 77 #include <userdat.hxx> 78 #include <markdata.hxx> 79 #include <globstr.hrc> 80 #include <scresid.hxx> 81 #include <scmod.hxx> 82 #include <postit.hxx> 83 #include <attrib.hxx> 84 #include <charthelper.hxx> 85 #include <table.hxx> 86 #include <stlpool.hxx> 87 #include <docpool.hxx> 88 #include <detfunc.hxx> 89 #include <basegfx/matrix/b2dhommatrix.hxx> 90 #include <clipparam.hxx> 91 92 #include <memory> 93 #include <algorithm> 94 #include <cstdlib> 95 96 namespace com::sun::star::embed { class XEmbeddedObject; } 97 98 #define DET_ARROW_OFFSET 1000 99 100 using namespace ::com::sun::star; 101 102 static E3dObjFactory* pF3d = nullptr; 103 static sal_uInt16 nInst = 0; 104 105 SfxObjectShell* ScDrawLayer::pGlobalDrawPersist = nullptr; 106 107 bool bDrawIsInUndo = false; //TODO: Member 108 109 ScUndoObjData::ScUndoObjData( SdrObject* pObjP, const ScAddress& rOS, const ScAddress& rOE, 110 const ScAddress& rNS, const ScAddress& rNE ) : 111 SdrUndoObj( *pObjP ), 112 aOldStt( rOS ), 113 aOldEnd( rOE ), 114 aNewStt( rNS ), 115 aNewEnd( rNE ) 116 { 117 } 118 119 ScUndoObjData::~ScUndoObjData() 120 { 121 } 122 123 void ScUndoObjData::Undo() 124 { 125 ScDrawObjData* pData = ScDrawLayer::GetObjData( mxObj.get() ); 126 OSL_ENSURE(pData,"ScUndoObjData: Data missing"); 127 if (pData) 128 { 129 pData->maStart = aOldStt; 130 pData->maEnd = aOldEnd; 131 } 132 133 // Undo also an untransformed anchor 134 pData = ScDrawLayer::GetNonRotatedObjData( mxObj.get() ); 135 if (pData) 136 { 137 pData->maStart = aOldStt; 138 pData->maEnd = aOldEnd; 139 } 140 } 141 142 void ScUndoObjData::Redo() 143 { 144 ScDrawObjData* pData = ScDrawLayer::GetObjData( mxObj.get() ); 145 OSL_ENSURE(pData,"ScUndoObjData: Data missing"); 146 if (pData) 147 { 148 pData->maStart = aNewStt; 149 pData->maEnd = aNewEnd; 150 } 151 152 // Redo also an untransformed anchor 153 pData = ScDrawLayer::GetNonRotatedObjData( mxObj.get() ); 154 if (pData) 155 { 156 pData->maStart = aNewStt; 157 pData->maEnd = aNewEnd; 158 } 159 } 160 161 ScUndoAnchorData::ScUndoAnchorData( SdrObject* pObjP, ScDocument* pDoc, SCTAB nTab ) : 162 SdrUndoObj( *pObjP ), 163 mpDoc( pDoc ), 164 mnTab( nTab ) 165 { 166 mbWasCellAnchored = ScDrawLayer::IsCellAnchored( *pObjP ); 167 mbWasResizeWithCell = ScDrawLayer::IsResizeWithCell( *pObjP ); 168 } 169 170 ScUndoAnchorData::~ScUndoAnchorData() 171 { 172 } 173 174 void ScUndoAnchorData::Undo() 175 { 176 // Trigger Object Change 177 if (mxObj->IsInserted() && mxObj->getSdrPageFromSdrObject()) 178 { 179 SdrHint aHint(SdrHintKind::ObjectChange, *mxObj); 180 mxObj->getSdrModelFromSdrObject().Broadcast(aHint); 181 } 182 183 if (mbWasCellAnchored) 184 ScDrawLayer::SetCellAnchoredFromPosition(*mxObj, *mpDoc, mnTab, mbWasResizeWithCell); 185 else 186 ScDrawLayer::SetPageAnchored( *mxObj ); 187 } 188 189 void ScUndoAnchorData::Redo() 190 { 191 if (mbWasCellAnchored) 192 ScDrawLayer::SetPageAnchored( *mxObj ); 193 else 194 ScDrawLayer::SetCellAnchoredFromPosition(*mxObj, *mpDoc, mnTab, mbWasResizeWithCell); 195 196 // Trigger Object Change 197 if (mxObj->IsInserted() && mxObj->getSdrPageFromSdrObject()) 198 { 199 SdrHint aHint(SdrHintKind::ObjectChange, *mxObj); 200 mxObj->getSdrModelFromSdrObject().Broadcast(aHint); 201 } 202 } 203 204 ScTabDeletedHint::ScTabDeletedHint( SCTAB nTabNo ) : 205 nTab( nTabNo ) 206 { 207 } 208 209 ScTabDeletedHint::~ScTabDeletedHint() 210 { 211 } 212 213 ScTabSizeChangedHint::ScTabSizeChangedHint( SCTAB nTabNo ) : 214 nTab( nTabNo ) 215 { 216 } 217 218 ScTabSizeChangedHint::~ScTabSizeChangedHint() 219 { 220 } 221 222 #define MAXMM 10000000 223 224 225 static void lcl_ReverseTwipsToMM( tools::Rectangle& rRect ) 226 { 227 rRect = o3tl::convert(rRect, o3tl::Length::mm100, o3tl::Length::twip); 228 } 229 230 static ScRange lcl_getClipRangeFromClipDoc(ScDocument* pClipDoc, SCTAB nClipTab) 231 { 232 if (!pClipDoc) 233 return ScRange(); 234 235 SCCOL nClipStartX; 236 SCROW nClipStartY; 237 SCCOL nClipEndX; 238 SCROW nClipEndY; 239 pClipDoc->GetClipStart(nClipStartX, nClipStartY); 240 pClipDoc->GetClipArea(nClipEndX, nClipEndY, true); 241 nClipEndX = nClipEndX + nClipStartX; 242 nClipEndY += nClipStartY; // GetClipArea returns the difference 243 244 return ScRange(nClipStartX, nClipStartY, nClipTab, nClipEndX, nClipEndY, nClipTab); 245 } 246 247 ScDrawLayer::ScDrawLayer( ScDocument* pDocument, OUString _aName ) : 248 FmFormModel( 249 nullptr, 250 pGlobalDrawPersist ? pGlobalDrawPersist : (pDocument ? pDocument->GetDocumentShell() : nullptr)), 251 aName(std::move( _aName )), 252 pDoc( pDocument ), 253 bRecording( false ), 254 bAdjustEnabled( true ), 255 bHyphenatorSet( false ) 256 { 257 SetVOCInvalidationIsReliable(true); 258 m_bThemedControls = false; 259 260 pGlobalDrawPersist = nullptr; // Only use once 261 262 SfxObjectShell* pObjSh = pDocument ? pDocument->GetDocumentShell() : nullptr; 263 XColorListRef pXCol = XColorList::GetStdColorList(); 264 if ( pObjSh ) 265 { 266 SetObjectShell( pObjSh ); 267 268 // set color table 269 const SvxColorListItem* pColItem = pObjSh->GetItem( SID_COLOR_TABLE ); 270 if ( pColItem ) 271 pXCol = pColItem->GetColorList(); 272 } 273 SetPropertyList( static_cast<XPropertyList *> (pXCol.get()) ); 274 275 SetSwapGraphics(); 276 277 SetScaleUnit(MapUnit::Map100thMM); 278 SfxItemPool& rPool = GetItemPool(); 279 rPool.SetDefaultMetric(MapUnit::Map100thMM); 280 SvxFrameDirectionItem aModeItem( SvxFrameDirection::Environment, EE_PARA_WRITINGDIR ); 281 rPool.SetPoolDefaultItem( aModeItem ); 282 283 // #i33700# 284 // Set shadow distance defaults as PoolDefaultItems. Details see bug. 285 rPool.SetPoolDefaultItem(makeSdrShadowXDistItem(300)); 286 rPool.SetPoolDefaultItem(makeSdrShadowYDistItem(300)); 287 288 // default for script spacing depends on locale, see SdDrawDocument ctor in sd 289 LanguageType eOfficeLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); 290 if (MsLangId::isKorean(eOfficeLanguage) || eOfficeLanguage == LANGUAGE_JAPANESE) 291 { 292 // secondary is edit engine pool 293 rPool.GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) ); 294 } 295 296 rPool.FreezeIdRanges(); // the pool is also used directly 297 298 SetStyleSheetPool(pDocument ? pDocument->GetStyleSheetPool() : new ScStyleSheetPool(rPool, pDocument)); 299 300 SdrLayerAdmin& rAdmin = GetLayerAdmin(); 301 rAdmin.NewLayer("vorne", SC_LAYER_FRONT.get()); 302 rAdmin.NewLayer("hinten", SC_LAYER_BACK.get()); 303 rAdmin.NewLayer("intern", SC_LAYER_INTERN.get()); 304 // tdf#140252 use same name as in ctor of SdrLayerAdmin 305 rAdmin.NewLayer(rAdmin.GetControlLayerName(), SC_LAYER_CONTROLS.get()); 306 rAdmin.NewLayer("hidden", SC_LAYER_HIDDEN.get()); 307 308 // Set link for URL-Fields 309 ScModule* pScMod = SC_MOD(); 310 Outliner& rOutliner = GetDrawOutliner(); 311 rOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); 312 rOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); 313 314 Outliner& rHitOutliner = GetHitTestOutliner(); 315 rHitOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); 316 rHitOutliner.SetStyleSheetPool(static_cast<SfxStyleSheetPool*>(GetStyleSheetPool())); 317 318 // set FontHeight pool defaults without changing static SdrEngineDefaults 319 SfxItemPool* pOutlinerPool = rOutliner.GetEditTextObjectPool(); 320 if ( pOutlinerPool ) 321 { 322 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt 323 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt 324 m_pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt 325 } 326 SfxItemPool* pHitOutlinerPool = rHitOutliner.GetEditTextObjectPool(); 327 if ( pHitOutlinerPool ) 328 { 329 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt 330 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK )); // 12Pt 331 pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL )); // 12Pt 332 } 333 334 // initial undo mode as in Calc document 335 if( pDoc ) 336 EnableUndo( pDoc->IsUndoEnabled() ); 337 338 // URL-Buttons have no handler anymore, all is done by themselves 339 340 if( !nInst++ ) 341 { 342 pF3d = new E3dObjFactory; 343 } 344 } 345 346 ScDrawLayer::~ScDrawLayer() 347 { 348 Broadcast(SdrHint(SdrHintKind::ModelCleared)); 349 350 ClearModel(true); 351 352 pUndoGroup.reset(); 353 if( !--nInst ) 354 { 355 delete pF3d; 356 pF3d = nullptr; 357 } 358 } 359 360 void ScDrawLayer::CreateDefaultStyles() 361 { 362 // Default 363 auto pSheet = &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_STANDARD), SfxStyleFamily::Frame, SfxStyleSearchBits::ScStandard); 364 SetDefaultStyleSheet(static_cast<SfxStyleSheet*>(pSheet)); 365 366 // Note 367 pSheet = &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_NOTE), SfxStyleFamily::Frame, SfxStyleSearchBits::ScStandard); 368 369 // caption tail arrow 370 ::basegfx::B2DPolygon aTriangle; 371 aTriangle.append(::basegfx::B2DPoint(10.0, 0.0)); 372 aTriangle.append(::basegfx::B2DPoint(0.0, 30.0)); 373 aTriangle.append(::basegfx::B2DPoint(20.0, 30.0)); 374 aTriangle.setClosed(true); 375 376 auto pSet = &pSheet->GetItemSet(); 377 pSet->Put(XLineStartItem(OUString(), ::basegfx::B2DPolyPolygon(aTriangle)).checkForUniqueItem(this)); 378 pSet->Put(XLineStartWidthItem(200)); 379 pSet->Put(XLineStartCenterItem(false)); 380 pSet->Put(XLineStyleItem(drawing::LineStyle_SOLID)); 381 pSet->Put(XFillStyleItem(drawing::FillStyle_SOLID)); 382 pSet->Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor())); 383 pSet->Put(SdrCaptionEscDirItem(SdrCaptionEscDir::BestFit)); 384 385 // shadow 386 pSet->Put(makeSdrShadowItem(true)); 387 pSet->Put(makeSdrShadowXDistItem(100)); 388 pSet->Put(makeSdrShadowYDistItem(100)); 389 390 // text attributes 391 pSet->Put(makeSdrTextLeftDistItem(100)); 392 pSet->Put(makeSdrTextRightDistItem(100)); 393 pSet->Put(makeSdrTextUpperDistItem(100)); 394 pSet->Put(makeSdrTextLowerDistItem(100)); 395 pSet->Put(makeSdrTextAutoGrowWidthItem(false)); 396 pSet->Put(makeSdrTextAutoGrowHeightItem(true)); 397 398 // text formatting 399 SfxItemSet aEditSet(GetItemPool()); 400 ScPatternAttr::FillToEditItemSet(aEditSet, pDoc->GetPool()->GetDefaultItem(ATTR_PATTERN).GetItemSet()); 401 402 pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO)); 403 pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO_CJK)); 404 pSet->Put(aEditSet.Get(EE_CHAR_FONTINFO_CTL)); 405 406 pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT)); 407 pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT_CJK)); 408 pSet->Put(aEditSet.Get(EE_CHAR_FONTHEIGHT_CTL)); 409 } 410 411 void ScDrawLayer::UseHyphenator() 412 { 413 if (!bHyphenatorSet) 414 { 415 css::uno::Reference< css::linguistic2::XHyphenator > 416 xHyphenator = LinguMgr::GetHyphenator(); 417 418 GetDrawOutliner().SetHyphenator( xHyphenator ); 419 GetHitTestOutliner().SetHyphenator( xHyphenator ); 420 421 bHyphenatorSet = true; 422 } 423 } 424 425 rtl::Reference<SdrPage> ScDrawLayer::AllocPage(bool bMasterPage) 426 { 427 return new ScDrawPage(*this, bMasterPage); 428 } 429 430 bool ScDrawLayer::HasObjects() const 431 { 432 bool bFound = false; 433 434 sal_uInt16 nCount = GetPageCount(); 435 for (sal_uInt16 i=0; i<nCount && !bFound; i++) 436 if (GetPage(i)->GetObjCount()) 437 bFound = true; 438 439 return bFound; 440 } 441 442 SdrModel* ScDrawLayer::AllocModel() const 443 { 444 // Allocated model (for clipboard etc) must not have a pointer 445 // to the original model's document, pass NULL as document: 446 auto pNewModel = std::make_unique<ScDrawLayer>(nullptr, aName); 447 auto pNewPool = static_cast<ScStyleSheetPool*>(pNewModel->GetStyleSheetPool()); 448 pNewPool->CopyUsedGraphicStylesFrom(GetStyleSheetPool()); 449 450 return pNewModel.release(); 451 } 452 453 bool ScDrawLayer::ScAddPage( SCTAB nTab ) 454 { 455 if (bDrawIsInUndo) 456 return false; // not inserted 457 458 rtl::Reference<ScDrawPage> pPage = static_cast<ScDrawPage*>(AllocPage( false ).get()); 459 InsertPage(pPage.get(), static_cast<sal_uInt16>(nTab)); 460 if (bRecording) 461 AddCalcUndo(std::make_unique<SdrUndoNewPage>(*pPage)); 462 463 ResetTab(nTab, pDoc->GetTableCount()-1); 464 return true; // inserted 465 } 466 467 void ScDrawLayer::ScRemovePage( SCTAB nTab ) 468 { 469 if (bDrawIsInUndo) 470 return; 471 472 Broadcast( ScTabDeletedHint( nTab ) ); 473 if (bRecording) 474 { 475 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 476 AddCalcUndo(std::make_unique<SdrUndoDelPage>(*pPage)); // Undo-Action becomes the page owner 477 RemovePage( static_cast<sal_uInt16>(nTab) ); // just deliver, not deleting 478 } 479 else 480 DeletePage( static_cast<sal_uInt16>(nTab) ); // just get rid of it 481 482 ResetTab(nTab, pDoc->GetTableCount()-1); 483 } 484 485 void ScDrawLayer::ScRenamePage( SCTAB nTab, const OUString& rNewName ) 486 { 487 ScDrawPage* pPage = static_cast<ScDrawPage*>( GetPage(static_cast<sal_uInt16>(nTab)) ); 488 if (pPage) 489 pPage->SetName(rNewName); 490 } 491 492 void ScDrawLayer::ScMovePage( sal_uInt16 nOldPos, sal_uInt16 nNewPos ) 493 { 494 MovePage( nOldPos, nNewPos ); 495 sal_uInt16 nMinPos = std::min(nOldPos, nNewPos); 496 ResetTab(nMinPos, pDoc->GetTableCount()-1); 497 } 498 499 void ScDrawLayer::ScCopyPage( sal_uInt16 nOldPos, sal_uInt16 nNewPos ) 500 { 501 if (bDrawIsInUndo) 502 return; 503 504 SdrPage* pOldPage = GetPage(nOldPos); 505 SdrPage* pNewPage = GetPage(nNewPos); 506 507 // Copying 508 509 if (pOldPage && pNewPage) 510 { 511 SCTAB nOldTab = static_cast<SCTAB>(nOldPos); 512 SCTAB nNewTab = static_cast<SCTAB>(nNewPos); 513 514 SdrObjListIter aIter( pOldPage, SdrIterMode::Flat ); 515 SdrObject* pOldObject = aIter.Next(); 516 while (pOldObject) 517 { 518 ScDrawObjData* pOldData = GetObjData(pOldObject); 519 if (pOldData) 520 { 521 pOldData->maStart.SetTab(nOldTab); 522 pOldData->maEnd.SetTab(nOldTab); 523 } 524 525 // Clone to target SdrModel 526 rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this)); 527 pNewObject->NbcMove(Size(0,0)); 528 pNewPage->InsertObject( pNewObject.get() ); 529 ScDrawObjData* pNewData = GetObjData(pNewObject.get()); 530 if (pNewData) 531 { 532 pNewData->maStart.SetTab(nNewTab); 533 pNewData->maEnd.SetTab(nNewTab); 534 } 535 536 if (bRecording) 537 AddCalcUndo( std::make_unique<SdrUndoInsertObj>( *pNewObject ) ); 538 539 pOldObject = aIter.Next(); 540 } 541 } 542 543 ResetTab(static_cast<SCTAB>(nNewPos), pDoc->GetTableCount()-1); 544 } 545 546 void ScDrawLayer::ResetTab( SCTAB nStart, SCTAB nEnd ) 547 { 548 SCTAB nPageSize = static_cast<SCTAB>(GetPageCount()); 549 if (nPageSize < 0) 550 // No drawing pages exist. 551 return; 552 553 if (nEnd >= nPageSize) 554 // Avoid iterating beyond the last existing page. 555 nEnd = nPageSize - 1; 556 557 for (SCTAB i = nStart; i <= nEnd; ++i) 558 { 559 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(i)); 560 if (!pPage) 561 continue; 562 563 SdrObjListIter aIter(pPage, SdrIterMode::Flat); 564 for (SdrObject* pObj = aIter.Next(); pObj; pObj = aIter.Next()) 565 { 566 ScDrawObjData* pData = GetObjData(pObj); 567 if (!pData) 568 continue; 569 570 pData->maStart.SetTab(i); 571 pData->maEnd.SetTab(i); 572 } 573 } 574 } 575 576 static bool IsInBlock( const ScAddress& rPos, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2 ) 577 { 578 return rPos.Col() >= nCol1 && rPos.Col() <= nCol2 && 579 rPos.Row() >= nRow1 && rPos.Row() <= nRow2; 580 } 581 582 void ScDrawLayer::MoveCells( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, 583 SCCOL nDx,SCROW nDy, bool bUpdateNoteCaptionPos ) 584 { 585 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 586 OSL_ENSURE(pPage,"Page not found"); 587 if (!pPage) 588 return; 589 590 bool bNegativePage = pDoc && pDoc->IsNegativePage( nTab ); 591 592 const size_t nCount = pPage->GetObjCount(); 593 for ( size_t i = 0; i < nCount; ++i ) 594 { 595 SdrObject* pObj = pPage->GetObj( i ); 596 ScDrawObjData* pData = GetObjDataTab( pObj, nTab ); 597 if( pData ) 598 { 599 const ScAddress aOldStt = pData->maStart; 600 const ScAddress aOldEnd = pData->maEnd; 601 bool bChange = false; 602 if ( aOldStt.IsValid() && IsInBlock( aOldStt, nCol1,nRow1, nCol2,nRow2 ) ) 603 { 604 pData->maStart.IncCol( nDx ); 605 pData->maStart.IncRow( nDy ); 606 bChange = true; 607 } 608 if ( aOldEnd.IsValid() && IsInBlock( aOldEnd, nCol1,nRow1, nCol2,nRow2 ) ) 609 { 610 pData->maEnd.IncCol( nDx ); 611 pData->maEnd.IncRow( nDy ); 612 bChange = true; 613 } 614 if (bChange) 615 { 616 if ( dynamic_cast<const SdrRectObj*>( pObj) != nullptr && pData->maStart.IsValid() && pData->maEnd.IsValid() ) 617 pData->maStart.PutInOrder( pData->maEnd ); 618 619 // Update also an untransformed anchor that's what we stored ( and still do ) to xml 620 ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData( pObj ); 621 if ( pNoRotatedAnchor ) 622 { 623 const ScAddress aOldSttNoRotatedAnchor = pNoRotatedAnchor->maStart; 624 const ScAddress aOldEndNoRotatedAnchor = pNoRotatedAnchor->maEnd; 625 if ( aOldSttNoRotatedAnchor.IsValid() && IsInBlock( aOldSttNoRotatedAnchor, nCol1,nRow1, nCol2,nRow2 ) ) 626 { 627 pNoRotatedAnchor->maStart.IncCol(nDx); 628 pNoRotatedAnchor->maStart.IncRow(nDy); 629 } 630 if ( aOldEndNoRotatedAnchor.IsValid() && IsInBlock( aOldEndNoRotatedAnchor, nCol1,nRow1, nCol2,nRow2 ) ) 631 { 632 pNoRotatedAnchor->maEnd.IncCol(nDx); 633 pNoRotatedAnchor->maEnd.IncRow(nDy); 634 } 635 } 636 637 AddCalcUndo( std::make_unique<ScUndoObjData>( pObj, aOldStt, aOldEnd, pData->maStart, pData->maEnd ) ); 638 RecalcPos( pObj, *pData, bNegativePage, bUpdateNoteCaptionPos ); 639 } 640 } 641 } 642 } 643 644 void ScDrawLayer::SetPageSize(sal_uInt16 nPageNo, const Size& rSize, bool bUpdateNoteCaptionPos, 645 const ScObjectHandling eObjectHandling) 646 { 647 SdrPage* pPage = GetPage(nPageNo); 648 if (!pPage) 649 return; 650 651 if ( rSize != pPage->GetSize() ) 652 { 653 pPage->SetSize( rSize ); 654 Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() on the views 655 } 656 657 // Do not call RecalcPos while loading, because row height is not finished, when SetPageSize 658 // is called first time. Instead the objects are initialized from ScXMLImport::endDocument() and 659 // RecalcPos is called from there. 660 if (!pDoc || pDoc->IsImportingXML()) 661 return; 662 663 // Implement Detective lines (adjust to new heights / widths) 664 // even if size is still the same 665 // (individual rows/columns can have been changed)) 666 667 bool bNegativePage = pDoc && pDoc->IsNegativePage( static_cast<SCTAB>(nPageNo) ); 668 669 // Disable mass broadcasts from drawing objects' position changes. 670 bool bWasLocked = isLocked(); 671 setLock(true); 672 673 const size_t nCount = pPage->GetObjCount(); 674 for ( size_t i = 0; i < nCount; ++i ) 675 { 676 SdrObject* pObj = pPage->GetObj( i ); 677 ScDrawObjData* pData = GetObjDataTab( pObj, static_cast<SCTAB>(nPageNo) ); 678 if( pData ) // cell anchored 679 { 680 if (pData->meType == ScDrawObjData::DrawingObject 681 || pData->meType == ScDrawObjData::ValidationCircle) 682 { 683 switch (eObjectHandling) 684 { 685 case ScObjectHandling::RecalcPosMode: 686 RecalcPos(pObj, *pData, bNegativePage, bUpdateNoteCaptionPos); 687 break; 688 case ScObjectHandling::MoveRTLMode: 689 MoveRTL(pObj); 690 break; 691 case ScObjectHandling::MirrorRTLMode: 692 MirrorRTL(pObj); 693 break; 694 } 695 } 696 else // DetectiveArrow and CellNote 697 RecalcPos(pObj, *pData, bNegativePage, bUpdateNoteCaptionPos); 698 } 699 else // page anchored 700 { 701 switch (eObjectHandling) 702 { 703 case ScObjectHandling::MoveRTLMode: 704 MoveRTL(pObj); 705 break; 706 case ScObjectHandling::MirrorRTLMode: 707 MirrorRTL(pObj); 708 break; 709 case ScObjectHandling::RecalcPosMode: // does not occur for page anchored shapes 710 break; 711 } 712 } 713 } 714 715 setLock(bWasLocked); 716 } 717 718 namespace 719 { 720 //Can't have a zero width dimension 721 tools::Rectangle lcl_makeSafeRectangle(const tools::Rectangle &rNew) 722 { 723 tools::Rectangle aRect = rNew; 724 if (aRect.Bottom() == aRect.Top()) 725 aRect.SetBottom( aRect.Top()+1 ); 726 if (aRect.Right() == aRect.Left()) 727 aRect.SetRight( aRect.Left()+1 ); 728 return aRect; 729 } 730 731 Point lcl_calcAvailableDiff(const ScDocument &rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const Point &aWantedDiff) 732 { 733 Point aAvailableDiff(aWantedDiff); 734 tools::Long nHeight = o3tl::convert(rDoc.GetRowHeight( nRow, nTab ), o3tl::Length::twip, o3tl::Length::mm100); 735 tools::Long nWidth = o3tl::convert(rDoc.GetColWidth( nCol, nTab ), o3tl::Length::twip, o3tl::Length::mm100); 736 if (aAvailableDiff.Y() > nHeight) 737 aAvailableDiff.setY( nHeight ); 738 if (aAvailableDiff.X() > nWidth) 739 aAvailableDiff.setX( nWidth ); 740 return aAvailableDiff; 741 } 742 743 tools::Rectangle lcl_UpdateCalcPoly(basegfx::B2DPolygon &rCalcPoly, int nWhichPoint, const Point &rPos) 744 { 745 rCalcPoly.setB2DPoint(nWhichPoint, basegfx::B2DPoint(rPos.X(), rPos.Y())); 746 basegfx::B2DRange aRange(basegfx::utils::getRange(rCalcPoly)); 747 return tools::Rectangle(static_cast<tools::Long>(aRange.getMinX()), static_cast<tools::Long>(aRange.getMinY()), 748 static_cast<tools::Long>(aRange.getMaxX()), static_cast<tools::Long>(aRange.getMaxY())); 749 } 750 751 bool lcl_AreRectanglesApproxEqual(const tools::Rectangle& rRectA, const tools::Rectangle& rRectB) 752 { 753 // Twips <-> Hmm conversions introduce +-1 differences although there are no real changes in the object. 754 // Therefore test with == is not appropriate in some cases. 755 if (std::abs(rRectA.Left() - rRectB.Left()) > 1) 756 return false; 757 if (std::abs(rRectA.Top() - rRectB.Top()) > 1) 758 return false; 759 if (std::abs(rRectA.Right() - rRectB.Right()) > 1) 760 return false; 761 if (std::abs(rRectA.Bottom() - rRectB.Bottom()) > 1) 762 return false; 763 return true; 764 } 765 766 bool lcl_NeedsMirrorYCorrection(const SdrObject* pObj) 767 { 768 return pObj->GetObjIdentifier() == SdrObjKind::CustomShape 769 && static_cast<const SdrObjCustomShape*>(pObj)->IsMirroredY(); 770 } 771 772 void lcl_SetLogicRectFromAnchor(SdrObject* pObj, const ScDrawObjData& rAnchor, const ScDocument* pDoc) 773 { 774 // This is only used during initialization. At that time, shape handling is always LTR. No need 775 // to consider negative page. 776 if (!pObj || !pDoc || !rAnchor.maEnd.IsValid() || !rAnchor.maStart.IsValid()) 777 return; 778 779 SCROW nHiddenRows = 0; 780 SCCOL nHiddenCols = 0; 781 // tdf#154005: Handle hidden row/col: remove hidden row/cols size from the ScDrawObjData shape size in case of forms 782 if (pObj->GetObjIdentifier() == SdrObjKind::UNO && pObj->GetObjInventor() == SdrInventor::FmForm) 783 { 784 nHiddenRows = pDoc->CountHiddenRows(rAnchor.maStart.Row(), rAnchor.maEnd.Row(), rAnchor.maStart.Tab()); 785 nHiddenCols = pDoc->CountHiddenCols(rAnchor.maStart.Col(), rAnchor.maEnd.Col(), rAnchor.maStart.Tab()); 786 } 787 788 // In case of a vertical mirrored custom shape, LibreOffice uses internally an additional 180deg 789 // in aGeo.nRotationAngle and in turn has a different logic rectangle position. We remove flip, 790 // set the logic rectangle, and apply flip again. You cannot simple use a 180deg-rotated 791 // rectangle, because custom shape mirroring is internally applied after the other 792 // transformations. 793 const bool bNeedsMirrorYCorrection = lcl_NeedsMirrorYCorrection(pObj); // remember state 794 if (bNeedsMirrorYCorrection) 795 { 796 const tools::Rectangle aRect(pObj->GetSnapRect()); 797 const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); 798 const Point aRight(aLeft.X() + 1000, aLeft.Y()); 799 pObj->NbcMirror(aLeft, aRight); 800 } 801 802 // Build full sized logic rectangle from start and end given in anchor. 803 const tools::Rectangle aStartCellRect( 804 pDoc->GetMMRect(rAnchor.maStart.Col(), rAnchor.maStart.Row(), rAnchor.maStart.Col(), 805 rAnchor.maStart.Row(), rAnchor.maStart.Tab(), false /*bHiddenAsZero*/)); 806 Point aStartPoint(aStartCellRect.Left(), aStartCellRect.Top()); 807 aStartPoint.AdjustX(rAnchor.maStartOffset.getX()); 808 aStartPoint.AdjustY(rAnchor.maStartOffset.getY()); 809 810 const tools::Rectangle aEndCellRect( 811 pDoc->GetMMRect(rAnchor.maEnd.Col() - nHiddenCols, rAnchor.maEnd.Row() - nHiddenRows, rAnchor.maEnd.Col() - nHiddenCols, 812 rAnchor.maEnd.Row() - nHiddenRows, rAnchor.maEnd.Tab(), false /*bHiddenAsZero*/)); 813 Point aEndPoint(aEndCellRect.Left(), aEndCellRect.Top()); 814 aEndPoint.AdjustX(rAnchor.maEndOffset.getX()); 815 aEndPoint.AdjustY(rAnchor.maEndOffset.getY()); 816 817 // Set this as new, full sized logical rectangle 818 tools::Rectangle aNewRectangle(aStartPoint, aEndPoint); 819 aNewRectangle.Normalize(); 820 if (!lcl_AreRectanglesApproxEqual(pObj->GetLogicRect(), aNewRectangle)) 821 pObj->NbcSetLogicRect(lcl_makeSafeRectangle(aNewRectangle)); 822 823 // The shape has the correct logical rectangle now. Reapply the above removed mirroring. 824 if (bNeedsMirrorYCorrection) 825 { 826 const tools::Rectangle aRect(pObj->GetSnapRect()); 827 const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); 828 const Point aRight(aLeft.X() + 1000, aLeft.Y()); 829 pObj->NbcMirror(aLeft, aRight); 830 } 831 } 832 833 } // namespace 834 835 void ScDrawLayer::ResizeLastRectFromAnchor(const SdrObject* pObj, ScDrawObjData& rData, 836 bool bNegativePage, bool bCanResize) 837 { 838 tools::Rectangle aRect = pObj->GetSnapRect(); 839 SCCOL nCol1 = rData.maStart.Col(); 840 SCROW nRow1 = rData.maStart.Row(); 841 SCTAB nTab1 = rData.maStart.Tab(); 842 SCCOL nCol2 = rData.maEnd.Col(); 843 SCROW nRow2 = rData.maEnd.Row(); 844 SCTAB nTab2 = rData.maEnd.Tab(); 845 Point aPos(pDoc->GetColOffset(nCol1, nTab1, /*bHiddenAsZero*/true), 846 pDoc->GetRowOffset(nRow1, nTab1, /*bHiddenAsZero*/true)); 847 aPos.setX(convertTwipToMm100(aPos.X())); 848 aPos.setY(convertTwipToMm100(aPos.Y())); 849 aPos += lcl_calcAvailableDiff(*pDoc, nCol1, nRow1, nTab1, rData.maStartOffset); 850 851 // this sets the needed changed position (translation) 852 aRect.SetPos(aPos); 853 854 if (bCanResize) 855 { 856 // all this stuff is additional stuff to evtl. not only translate the 857 // range (Rectangle), but also check for and evtl. do corrections for it's size 858 const tools::Rectangle aLastCellRect(rData.getLastCellRect()); 859 860 // If the row was hidden before, or we don't have a valid cell rect, calculate the 861 // new rect based on the end point. 862 // Also when the end point is set, we need to consider it. 863 if (rData.mbWasInHiddenRow || aLastCellRect.IsEmpty() || nRow1 != nRow2 || nCol1 != nCol2) 864 { 865 Point aEnd(pDoc->GetColOffset(nCol2, nTab2, /*bHiddenAsZero*/true), 866 pDoc->GetRowOffset(nRow2, nTab2, /*bHiddenAsZero*/true)); 867 aEnd.setX(convertTwipToMm100(aEnd.X())); 868 aEnd.setY(convertTwipToMm100(aEnd.Y())); 869 aEnd += lcl_calcAvailableDiff(*pDoc, nCol2, nRow2, nTab2, rData.maEndOffset); 870 871 aRect = tools::Rectangle(aPos, aEnd); 872 } 873 else if (!aLastCellRect.IsEmpty()) 874 { 875 // We calculate based on the last cell rect to be able to scale the image 876 // as much as the cell was scaled. 877 // Still, we keep the image in its current cell (to keep start anchor == end anchor) 878 const tools::Rectangle aCurrentCellRect(GetCellRect(*GetDocument(), rData.maStart, true)); 879 tools::Long nCurrentWidth(aCurrentCellRect.GetWidth()); 880 tools::Long nCurrentHeight(aCurrentCellRect.GetHeight()); 881 const tools::Long nLastWidth(aLastCellRect.GetWidth()); 882 const tools::Long nLastHeight(aLastCellRect.GetHeight()); 883 884 // tdf#116931 Avoid and correct nifty numerical problems with the integer 885 // based and converted values (GetCellRect uses multiplies with HMM_PER_TWIPS) 886 if(nCurrentWidth + 1 == nLastWidth || nCurrentWidth == nLastWidth + 1) 887 { 888 nCurrentWidth = nLastWidth; 889 } 890 891 if(nCurrentHeight + 1 == nLastHeight || nCurrentHeight == nLastHeight + 1) 892 { 893 nCurrentHeight = nLastHeight; 894 } 895 896 // get initial ScalingFactors 897 double fWidthFactor(nCurrentWidth == nLastWidth || 0 == nLastWidth 898 ? 1.0 899 : static_cast<double>(nCurrentWidth) / static_cast<double>(nLastWidth)); 900 double fHeightFactor(nCurrentHeight == nLastHeight || 0 == nLastHeight 901 ? 1.0 902 : static_cast<double>(nCurrentHeight) / static_cast<double>(nLastHeight)); 903 904 // check if we grow or shrink - and at all 905 const bool bIsGrowing(nCurrentWidth > nLastWidth || nCurrentHeight > nLastHeight); 906 const bool bIsShrinking(nCurrentWidth < nLastWidth || nCurrentHeight < nLastHeight); 907 const bool bIsSizeChanged(bIsGrowing || bIsShrinking); 908 909 // handle AspectRatio, only needed if size does change 910 if(bIsSizeChanged && pObj->shouldKeepAspectRatio()) 911 { 912 tools::Rectangle aRectIncludingOffset = aRect; 913 aRectIncludingOffset.setWidth(aRect.GetWidth() + rData.maStartOffset.X()); 914 aRectIncludingOffset.setHeight(aRect.GetHeight() + rData.maStartOffset.Y()); 915 tools::Long nWidth = aRectIncludingOffset.GetWidth(); 916 assert(nWidth && "div-by-zero"); 917 double fMaxWidthFactor = static_cast<double>(nCurrentWidth) 918 / static_cast<double>(nWidth); 919 tools::Long nHeight = aRectIncludingOffset.GetHeight(); 920 assert(nHeight && "div-by-zero"); 921 double fMaxHeightFactor = static_cast<double>(nCurrentHeight) 922 / static_cast<double>(nHeight); 923 double fMaxFactor = std::min(fMaxHeightFactor, fMaxWidthFactor); 924 925 if(bIsGrowing) // cell is growing larger 926 { 927 // To actually grow the image, we need to take the max 928 fWidthFactor = std::max(fWidthFactor, fHeightFactor); 929 } 930 else if(bIsShrinking) // cell is growing smaller, take the min 931 { 932 fWidthFactor = std::min(fWidthFactor, fHeightFactor); 933 } 934 935 // We don't want the image to become larger than the current cell 936 fWidthFactor = fHeightFactor = std::min(fWidthFactor, fMaxFactor); 937 } 938 939 if(bIsSizeChanged) 940 { 941 // tdf#116931 re-organized scaling (if needed) 942 // Check if we need to scale at all. Always scale on growing. 943 bool bNeedToScale(bIsGrowing); 944 945 if(!bNeedToScale && bIsShrinking) 946 { 947 // Check if original still fits into space. Do *not* forget to 948 // compare with evtl. numerically corrected aCurrentCellRect 949 const bool bFitsInX(aRect.Right() <= aCurrentCellRect.Left() + nCurrentWidth); 950 const bool bFitsInY(aRect.Bottom() <= aCurrentCellRect.Top() + nCurrentHeight); 951 952 // If the image still fits in the smaller cell, don't resize it at all 953 bNeedToScale = (!bFitsInX || !bFitsInY); 954 } 955 956 if(bNeedToScale) 957 { 958 // tdf#116931 use transformations now. Translation is already applied 959 // (see aRect.SetPos above), so only scale needs to be applied - relative 960 // to *new* CellRect (which is aCurrentCellRect). 961 // Prepare scale relative to top-left of aCurrentCellRect 962 basegfx::B2DHomMatrix aChange; 963 964 aChange.translate(-aCurrentCellRect.Left(), -aCurrentCellRect.Top()); 965 aChange.scale(fWidthFactor, fHeightFactor); 966 aChange.translate(aCurrentCellRect.Left(), aCurrentCellRect.Top()); 967 968 // create B2DRange and transform by prepared scale 969 basegfx::B2DRange aNewRange = vcl::unotools::b2DRectangleFromRectangle(aRect); 970 971 aNewRange.transform(aChange); 972 973 // apply to aRect 974 aRect = tools::Rectangle( 975 basegfx::fround(aNewRange.getMinX()), basegfx::fround(aNewRange.getMinY()), 976 basegfx::fround(aNewRange.getMaxX()), basegfx::fround(aNewRange.getMaxY())); 977 } 978 } 979 } 980 } 981 982 if (bNegativePage) 983 MirrorRectRTL(aRect); 984 985 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect), pObj->IsVisible()); 986 } 987 988 void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rData) 989 { 990 // This is called from ScXMLImport::endDocument() 991 if (!pDoc || !pObj) 992 return; 993 if (!rData.getShapeRect().IsEmpty()) 994 return; // already initialized, should not happen 995 if (rData.meType == ScDrawObjData::CellNote || rData.meType == ScDrawObjData::ValidationCircle 996 || rData.meType == ScDrawObjData::DetectiveArrow) 997 return; // handled in RecalcPos 998 999 // Prevent multiple broadcasts during the series of changes. 1000 bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked(); 1001 pObj->getSdrModelFromSdrObject().setLock(true); 1002 1003 // rNoRotatedAnchor refers in its start and end addresses and its start and end offsets to 1004 // the logic rectangle of the object. The values are so, as if no hidden columns and rows 1005 // exists and if it is a LTR sheet. These values are directly used for XML in ODF file. 1006 ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData(pObj, true /*bCreate*/); 1007 1008 // From XML import, rData contains temporarily the anchor information as they are given in 1009 // XML. Copy it to rNoRotatedAnchor, where it belongs. rData will later contain the anchor 1010 // of the transformed object as visible on screen. 1011 rNoRotatedAnchor.maStart = rData.maStart; 1012 rNoRotatedAnchor.maEnd = rData.maEnd; 1013 rNoRotatedAnchor.maStartOffset = rData.maStartOffset; 1014 rNoRotatedAnchor.maEndOffset = rData.maEndOffset; 1015 1016 SCCOL nCol1 = rNoRotatedAnchor.maStart.Col(); 1017 SCROW nRow1 = rNoRotatedAnchor.maStart.Row(); 1018 SCTAB nTab1 = rNoRotatedAnchor.maStart.Tab(); // Used as parameter several times 1019 1020 // Object has coordinates relative to left/top of containing cell in XML. Change object to 1021 // absolute coordinates as internally used. 1022 const tools::Rectangle aRect( 1023 pDoc->GetMMRect(nCol1, nRow1, nCol1, nRow1, nTab1, false /*bHiddenAsZero*/)); 1024 const Size aShift(aRect.Left(), aRect.Top()); 1025 pObj->NbcMove(aShift); 1026 1027 const ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObj); 1028 if (aAnchorType == SCA_CELL_RESIZE) 1029 { 1030 if (pObj->GetObjIdentifier() == SdrObjKind::Line) 1031 { 1032 // Horizontal lines might have wrong start and end anchor because of erroneously applied 1033 // 180deg rotation (tdf#137446). Other lines have wrong end anchor. Coordinates in 1034 // object are correct. Use them for recreating the anchor. 1035 const basegfx::B2DPolygon aPoly( 1036 static_cast<SdrPathObj*>(pObj)->GetPathPoly().getB2DPolygon(0)); 1037 const basegfx::B2DPoint aB2DPoint0(aPoly.getB2DPoint(0)); 1038 const basegfx::B2DPoint aB2DPoint1(aPoly.getB2DPoint(1)); 1039 const Point aPointLT(FRound(std::min(aB2DPoint0.getX(), aB2DPoint1.getX())), 1040 FRound(std::min(aB2DPoint0.getY(), aB2DPoint1.getY()))); 1041 const Point aPointRB(FRound(std::max(aB2DPoint0.getX(), aB2DPoint1.getX())), 1042 FRound(std::max(aB2DPoint0.getY(), aB2DPoint1.getY()))); 1043 const tools::Rectangle aObjRect(aPointLT, aPointRB); 1044 GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, nTab1, 1045 false /*bHiddenAsZero*/); 1046 } 1047 else if (pObj->GetObjIdentifier() == SdrObjKind::Measure) 1048 { 1049 // Measure lines might have got wrong start and end anchor from XML import. Recreate 1050 // anchor from start and end point. 1051 SdrMeasureObj* pMeasureObj = static_cast<SdrMeasureObj*>(pObj); 1052 // tdf#137576. The logic rectangle has likely no current values here, but only the 1053 // 1cm x 1cm default size. The call of TakeUnrotatedSnapRect is currently (LO 7.2) 1054 // the only way to force a recalc of the logic rectangle. 1055 tools::Rectangle aObjRect; 1056 pMeasureObj->TakeUnrotatedSnapRect(aObjRect); 1057 GetCellAnchorFromPosition(aObjRect, rNoRotatedAnchor, *pDoc, rData.maStart.Tab(), 1058 false /*bHiddenAsZero*/); 1059 } 1060 else 1061 { 1062 // In case there are hidden rows or cols, versions 7.0 and earlier have written width and 1063 // height in file so that hidden row or col are count as zero. XML import bases the 1064 // logical rectangle of the object on it. Shapes have at least wrong size, when row or col 1065 // are shown. We try to regenerate the logic rectangle as far as possible from the anchor. 1066 // ODF specifies anyway, that the size has to be ignored, if end cell attributes exist. 1067 lcl_SetLogicRectFromAnchor(pObj, rNoRotatedAnchor, pDoc); 1068 } 1069 } 1070 else // aAnchorType == SCA_CELL, other types will not occur here. 1071 { 1072 // XML has no end cell address in this case. We generate it from position. 1073 UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1, 1074 true /*bUseLogicRect*/); 1075 } 1076 1077 // Make sure maShapeRect of rNoRotatedAnchor is not empty. Method ScDrawView::Notify() 1078 // needs it to detect a change in object geometry. For example a 180deg rotation effects only 1079 // logic rect. 1080 rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), true); 1081 1082 // Start and end addresses and offsets in rData refer to the actual snap rectangle of the 1083 // shape. We initialize them here based on the "full" sized object. Adaptation to reduced size 1084 // (by hidden row/col) is done later in RecalcPos. 1085 GetCellAnchorFromPosition(pObj->GetSnapRect(), rData, *pDoc, nTab1, false /*bHiddenAsZero*/); 1086 1087 // As of ODF 1.3 strict there is no attribute to store whether an object is hidden. So a "visible" 1088 // object might actually be hidden by being in hidden row or column. We detect it here. 1089 // Note, that visibility by hidden row or column refers to the snap rectangle. 1090 if (pObj->IsVisible() 1091 && (pDoc->RowHidden(rData.maStart.Row(), rData.maStart.Tab()) 1092 || pDoc->ColHidden(rData.maStart.Col(), rData.maStart.Tab()))) 1093 pObj->SetVisible(false); 1094 1095 // Set visibility. ToDo: Really used? 1096 rNoRotatedAnchor.setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible()); 1097 1098 // And set maShapeRect in rData. It stores not only the current rectangles, but currently, 1099 // existence of maShapeRect is the flag for initialization is done. 1100 rData.setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible()); 1101 1102 pObj->getSdrModelFromSdrObject().setLock(bWasLocked); 1103 } 1104 1105 void ScDrawLayer::RecalcPos( SdrObject* pObj, ScDrawObjData& rData, bool bNegativePage, bool bUpdateNoteCaptionPos ) 1106 { 1107 OSL_ENSURE( pDoc, "ScDrawLayer::RecalcPos - missing document" ); 1108 if( !pDoc ) 1109 return; 1110 1111 if (rData.meType == ScDrawObjData::CellNote) 1112 { 1113 OSL_ENSURE( rData.maStart.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" ); 1114 /* #i109372# On insert/remove rows/columns/cells: Updating the caption 1115 position must not be done, if the cell containing the note has not 1116 been moved yet in the document. The calling code now passes an 1117 additional boolean stating if the cells are already moved. */ 1118 /* tdf #152081 Do not change hidden objects. That would produce zero height 1119 or width and loss of caption.*/ 1120 if (bUpdateNoteCaptionPos && pObj->IsVisible()) 1121 { 1122 /* When inside an undo action, there may be pending note captions 1123 where cell note is already deleted (thus document cannot find 1124 the note object anymore). The caption will be deleted later 1125 with drawing undo. */ 1126 if( ScPostIt* pNote = pDoc->GetNote( rData.maStart ) ) 1127 pNote->UpdateCaptionPos( rData.maStart ); 1128 } 1129 return; 1130 } 1131 1132 bool bValid1 = rData.maStart.IsValid(); 1133 SCCOL nCol1 = rData.maStart.Col(); 1134 SCROW nRow1 = rData.maStart.Row(); 1135 SCTAB nTab1 = rData.maStart.Tab(); 1136 bool bValid2 = rData.maEnd.IsValid(); 1137 SCCOL nCol2 = rData.maEnd.Col(); 1138 SCROW nRow2 = rData.maEnd.Row(); 1139 SCTAB nTab2 = rData.maEnd.Tab(); 1140 1141 if (rData.meType == ScDrawObjData::ValidationCircle) 1142 { 1143 // Validation circle for detective. 1144 rData.setShapeRect(GetDocument(), pObj->GetLogicRect()); 1145 1146 // rData.maStart should contain the address of the be validated cell. 1147 tools::Rectangle aRect = GetCellRect(*GetDocument(), rData.maStart, true); 1148 aRect.AdjustLeft( -250 ); 1149 aRect.AdjustRight(250 ); 1150 aRect.AdjustTop( -70 ); 1151 aRect.AdjustBottom(70 ); 1152 if ( bNegativePage ) 1153 MirrorRectRTL( aRect ); 1154 1155 if ( pObj->GetLogicRect() != aRect ) 1156 { 1157 if (bRecording) 1158 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1159 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect)); 1160 // maStart has the meaning of "to be validated cell" in a validation circle. For usual 1161 // drawing objects it has the meaning "left/top of logic/snap rect". Because the rectangle 1162 // is expanded above, SetLogicRect() will set maStart to one cell left and one cell above 1163 // of the to be validated cell. We need to backup the old value and restore it. 1164 ScAddress aBackup(rData.maStart); 1165 pObj->SetLogicRect(rData.getShapeRect()); 1166 rData.maStart = aBackup; 1167 } 1168 } 1169 else if (rData.meType == ScDrawObjData::DetectiveArrow) 1170 { 1171 rData.setShapeRect(GetDocument(), pObj->GetLogicRect()); 1172 basegfx::B2DPolygon aCalcPoly; 1173 Point aOrigStartPos(pObj->GetPoint(0)); 1174 Point aOrigEndPos(pObj->GetPoint(1)); 1175 aCalcPoly.append(basegfx::B2DPoint(aOrigStartPos.X(), aOrigStartPos.Y())); 1176 aCalcPoly.append(basegfx::B2DPoint(aOrigEndPos.X(), aOrigEndPos.Y())); 1177 //TODO: do not create multiple Undos for one object (last one can be omitted then) 1178 1179 SCCOL nLastCol; 1180 SCROW nLastRow; 1181 if( bValid1 ) 1182 { 1183 Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); 1184 if (!pDoc->ColHidden(nCol1, nTab1, nullptr, &nLastCol)) 1185 aPos.AdjustX(pDoc->GetColWidth( nCol1, nTab1 ) / 4 ); 1186 if (!pDoc->RowHidden(nRow1, nTab1, nullptr, &nLastRow)) 1187 aPos.AdjustY(pDoc->GetRowHeight( nRow1, nTab1 ) / 2 ); 1188 aPos.setX(convertTwipToMm100(aPos.X())); 1189 aPos.setY(convertTwipToMm100(aPos.Y())); 1190 Point aStartPos = aPos; 1191 if ( bNegativePage ) 1192 aStartPos.setX( -aStartPos.X() ); // don't modify aPos - used below 1193 if ( pObj->GetPoint( 0 ) != aStartPos ) 1194 { 1195 if (bRecording) 1196 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1197 1198 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos)); 1199 pObj->SetPoint( aStartPos, 0 ); 1200 } 1201 1202 if( !bValid2 ) 1203 { 1204 Point aEndPos( aPos.X() + DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); 1205 if (aEndPos.Y() < 0) 1206 aEndPos.AdjustY(2 * DET_ARROW_OFFSET); 1207 if ( bNegativePage ) 1208 aEndPos.setX( -aEndPos.X() ); 1209 if ( pObj->GetPoint( 1 ) != aEndPos ) 1210 { 1211 if (bRecording) 1212 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1213 1214 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos)); 1215 pObj->SetPoint( aEndPos, 1 ); 1216 } 1217 } 1218 } 1219 if( bValid2 ) 1220 { 1221 Point aPos( pDoc->GetColOffset( nCol2, nTab2 ), pDoc->GetRowOffset( nRow2, nTab2 ) ); 1222 if (!pDoc->ColHidden(nCol2, nTab2, nullptr, &nLastCol)) 1223 aPos.AdjustX(pDoc->GetColWidth( nCol2, nTab2 ) / 4 ); 1224 if (!pDoc->RowHidden(nRow2, nTab2, nullptr, &nLastRow)) 1225 aPos.AdjustY(pDoc->GetRowHeight( nRow2, nTab2 ) / 2 ); 1226 aPos.setX(convertTwipToMm100(aPos.X())); 1227 aPos.setY(convertTwipToMm100(aPos.Y())); 1228 Point aEndPos = aPos; 1229 if ( bNegativePage ) 1230 aEndPos.setX( -aEndPos.X() ); // don't modify aPos - used below 1231 if ( pObj->GetPoint( 1 ) != aEndPos ) 1232 { 1233 if (bRecording) 1234 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1235 1236 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 1, aEndPos)); 1237 pObj->SetPoint( aEndPos, 1 ); 1238 } 1239 1240 if( !bValid1 ) 1241 { 1242 Point aStartPos( aPos.X() - DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); 1243 if (aStartPos.X() < 0) 1244 aStartPos.AdjustX(2 * DET_ARROW_OFFSET); 1245 if (aStartPos.Y() < 0) 1246 aStartPos.AdjustY(2 * DET_ARROW_OFFSET); 1247 if ( bNegativePage ) 1248 aStartPos.setX( -aStartPos.X() ); 1249 if ( pObj->GetPoint( 0 ) != aStartPos ) 1250 { 1251 if (bRecording) 1252 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1253 1254 rData.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly, 0, aStartPos)); 1255 pObj->SetPoint( aStartPos, 0 ); 1256 } 1257 } 1258 } 1259 } // end ScDrawObjData::DetectiveArrow 1260 else // start ScDrawObjData::DrawingObject 1261 { 1262 // Do not change hidden objects. That would produce zero height or width and loss of offsets. 1263 if (!pObj->IsVisible()) 1264 return; 1265 1266 // Prevent multiple broadcasts during the series of changes. 1267 bool bWasLocked = pObj->getSdrModelFromSdrObject().isLocked(); 1268 pObj->getSdrModelFromSdrObject().setLock(true); 1269 1270 bool bCanResize = bValid2 && !pObj->IsResizeProtect() && rData.mbResizeWithCell; 1271 1272 // update anchor with snap rect 1273 ResizeLastRectFromAnchor( pObj, rData, bNegativePage, bCanResize ); 1274 1275 ScDrawObjData& rNoRotatedAnchor = *GetNonRotatedObjData( pObj, true /*bCreate*/); 1276 1277 if( bCanResize ) 1278 { 1279 tools::Rectangle aNew = rData.getShapeRect(); 1280 tools::Rectangle aOld(pObj->GetSnapRect()); 1281 if (!lcl_AreRectanglesApproxEqual(aNew, aOld)) 1282 { 1283 if (bRecording) 1284 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1285 1286 // ToDo: Implement NbcSetSnapRect of SdrMeasureObj. Then this can be removed. 1287 tools::Long nOldWidth = aOld.GetWidth(); 1288 tools::Long nOldHeight = aOld.GetHeight(); 1289 if (pObj->IsPolyObj() && nOldWidth && nOldHeight) 1290 { 1291 // Polyline objects need special treatment. 1292 Size aSizeMove(aNew.Left()-aOld.Left(), aNew.Top()-aOld.Top()); 1293 pObj->NbcMove(aSizeMove); 1294 1295 double fXFrac = static_cast<double>(aNew.GetWidth()) / static_cast<double>(nOldWidth); 1296 double fYFrac = static_cast<double>(aNew.GetHeight()) / static_cast<double>(nOldHeight); 1297 pObj->NbcResize(aNew.TopLeft(), Fraction(fXFrac), Fraction(fYFrac)); 1298 } 1299 1300 rData.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData.getShapeRect()), pObj->IsVisible()); 1301 if (pObj->GetObjIdentifier() == SdrObjKind::CustomShape) 1302 pObj->AdjustToMaxRect(rData.getShapeRect()); 1303 else 1304 pObj->SetSnapRect(rData.getShapeRect()); 1305 1306 // The shape rectangle in the 'unrotated' anchor needs to be updated to the changed 1307 // object geometry. It is used in adjustAnchoredPosition() in ScDrawView::Notify(). 1308 rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible()); 1309 } 1310 } 1311 else 1312 { 1313 const Point aPos(rData.getShapeRect().TopLeft()); 1314 if ( pObj->GetRelativePos() != aPos ) 1315 { 1316 if (bRecording) 1317 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 1318 pObj->SetRelativePos( aPos ); 1319 rNoRotatedAnchor.setShapeRect(pDoc, pObj->GetLogicRect(), pObj->IsVisible()); 1320 } 1321 } 1322 /* 1323 * If we were not allowed resize the object, then the end cell anchor 1324 * is possibly incorrect now, and if the object has no end-cell (e.g. 1325 * missing in original .xml) we are also forced to generate one 1326 */ 1327 bool bEndAnchorIsBad = !bValid2 || pObj->IsResizeProtect(); 1328 if (bEndAnchorIsBad) 1329 { 1330 // update 'rotated' anchor 1331 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rData, *pDoc, nTab1, false); 1332 // update 'unrotated' anchor 1333 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1 ); 1334 } 1335 1336 // End prevent multiple broadcasts during the series of changes. 1337 pObj->getSdrModelFromSdrObject().setLock(bWasLocked); 1338 if (!bWasLocked) 1339 pObj->BroadcastObjectChange(); 1340 } // end ScDrawObjData::DrawingObject 1341 } 1342 1343 bool ScDrawLayer::GetPrintArea( ScRange& rRange, bool bSetHor, bool bSetVer ) const 1344 { 1345 OSL_ENSURE( pDoc, "ScDrawLayer::GetPrintArea without document" ); 1346 if ( !pDoc ) 1347 return false; 1348 1349 SCTAB nTab = rRange.aStart.Tab(); 1350 OSL_ENSURE( rRange.aEnd.Tab() == nTab, "GetPrintArea: Tab differ" ); 1351 1352 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1353 1354 bool bAny = false; 1355 tools::Long nEndX = 0; 1356 tools::Long nEndY = 0; 1357 tools::Long nStartX = LONG_MAX; 1358 tools::Long nStartY = LONG_MAX; 1359 1360 // Calculate borders 1361 1362 if (!bSetHor) 1363 { 1364 nStartX = 0; 1365 SCCOL nStartCol = rRange.aStart.Col(); 1366 SCCOL i; 1367 for (i=0; i<nStartCol; i++) 1368 nStartX +=pDoc->GetColWidth(i,nTab); 1369 nEndX = nStartX; 1370 SCCOL nEndCol = rRange.aEnd.Col(); 1371 for (i=nStartCol; i<=nEndCol; i++) 1372 nEndX += pDoc->GetColWidth(i,nTab); 1373 nStartX = convertTwipToMm100(nStartX); 1374 nEndX = convertTwipToMm100(nEndX); 1375 } 1376 if (!bSetVer) 1377 { 1378 nStartY = pDoc->GetRowHeight( 0, rRange.aStart.Row()-1, nTab); 1379 nEndY = nStartY + pDoc->GetRowHeight( rRange.aStart.Row(), 1380 rRange.aEnd.Row(), nTab); 1381 nStartY = convertTwipToMm100(nStartY); 1382 nEndY = convertTwipToMm100(nEndY); 1383 } 1384 1385 if ( bNegativePage ) 1386 { 1387 nStartX = -nStartX; // positions are negative, swap start/end so the same comparisons work 1388 nEndX = -nEndX; 1389 ::std::swap( nStartX, nEndX ); 1390 } 1391 1392 const SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1393 OSL_ENSURE(pPage,"Page not found"); 1394 if (pPage) 1395 { 1396 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1397 SdrObject* pObject = aIter.Next(); 1398 while (pObject) 1399 { 1400 //TODO: test Flags (hidden?) 1401 1402 tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); 1403 bool bFit = true; 1404 if ( !bSetHor && ( aObjRect.Right() < nStartX || aObjRect.Left() > nEndX ) ) 1405 bFit = false; 1406 if ( !bSetVer && ( aObjRect.Bottom() < nStartY || aObjRect.Top() > nEndY ) ) 1407 bFit = false; 1408 // #i104716# don't include hidden note objects 1409 if ( bFit && pObject->GetLayer() != SC_LAYER_HIDDEN ) 1410 { 1411 if (bSetHor) 1412 { 1413 if (aObjRect.Left() < nStartX) nStartX = aObjRect.Left(); 1414 if (aObjRect.Right() > nEndX) nEndX = aObjRect.Right(); 1415 } 1416 if (bSetVer) 1417 { 1418 if (aObjRect.Top() < nStartY) nStartY = aObjRect.Top(); 1419 if (aObjRect.Bottom() > nEndY) nEndY = aObjRect.Bottom(); 1420 } 1421 bAny = true; 1422 } 1423 1424 pObject = aIter.Next(); 1425 } 1426 } 1427 1428 if ( bNegativePage ) 1429 { 1430 nStartX = -nStartX; // reverse transformation, so the same cell address calculation works 1431 nEndX = -nEndX; 1432 ::std::swap( nStartX, nEndX ); 1433 } 1434 1435 if (bAny) 1436 { 1437 OSL_ENSURE( nStartX<=nEndX && nStartY<=nEndY, "Start/End wrong in ScDrawLayer::GetPrintArea" ); 1438 1439 if (bSetHor) 1440 { 1441 nStartX = o3tl::toTwips(nStartX, o3tl::Length::mm100); 1442 nEndX = o3tl::toTwips(nEndX, o3tl::Length::mm100); 1443 tools::Long nWidth; 1444 1445 nWidth = 0; 1446 rRange.aStart.SetCol( 0 ); 1447 if (nWidth <= nStartX) 1448 { 1449 for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, pDoc->MaxCol())) 1450 { 1451 nWidth += pDoc->GetColWidth(nCol,nTab); 1452 if (nWidth > nStartX) 1453 { 1454 rRange.aStart.SetCol( nCol ); 1455 break; 1456 } 1457 } 1458 } 1459 1460 nWidth = 0; 1461 rRange.aEnd.SetCol( 0 ); 1462 if (nWidth <= nEndX) 1463 { 1464 for (SCCOL nCol : pDoc->GetColumnsRange(nTab, 0, pDoc->MaxCol())) //TODO: start at Start 1465 { 1466 nWidth += pDoc->GetColWidth(nCol,nTab); 1467 if (nWidth > nEndX) 1468 { 1469 rRange.aEnd.SetCol( nCol ); 1470 break; 1471 } 1472 } 1473 } 1474 } 1475 1476 if (bSetVer) 1477 { 1478 nStartY = o3tl::toTwips(nStartY, o3tl::Length::mm100); 1479 nEndY = o3tl::toTwips(nEndY, o3tl::Length::mm100); 1480 SCROW nRow = pDoc->GetRowForHeight( nTab, nStartY); 1481 rRange.aStart.SetRow( nRow>0 ? (nRow-1) : 0); 1482 nRow = pDoc->GetRowForHeight( nTab, nEndY); 1483 rRange.aEnd.SetRow( nRow == pDoc->MaxRow() ? pDoc->MaxRow() : 1484 (nRow>0 ? (nRow-1) : 0)); 1485 } 1486 } 1487 else 1488 { 1489 if (bSetHor) 1490 { 1491 rRange.aStart.SetCol(0); 1492 rRange.aEnd.SetCol(0); 1493 } 1494 if (bSetVer) 1495 { 1496 rRange.aStart.SetRow(0); 1497 rRange.aEnd.SetRow(0); 1498 } 1499 } 1500 return bAny; 1501 } 1502 1503 void ScDrawLayer::AddCalcUndo( std::unique_ptr<SdrUndoAction> pUndo ) 1504 { 1505 if (bRecording) 1506 { 1507 if (!pUndoGroup) 1508 pUndoGroup.reset(new SdrUndoGroup(*this)); 1509 1510 pUndoGroup->AddAction( std::move(pUndo) ); 1511 } 1512 } 1513 1514 void ScDrawLayer::BeginCalcUndo(bool bDisableTextEditUsesCommonUndoManager) 1515 { 1516 SetDisableTextEditUsesCommonUndoManager(bDisableTextEditUsesCommonUndoManager); 1517 pUndoGroup.reset(); 1518 bRecording = true; 1519 } 1520 1521 std::unique_ptr<SdrUndoGroup> ScDrawLayer::GetCalcUndo() 1522 { 1523 std::unique_ptr<SdrUndoGroup> pRet = std::move(pUndoGroup); 1524 bRecording = false; 1525 SetDisableTextEditUsesCommonUndoManager(false); 1526 return pRet; 1527 } 1528 1529 void ScDrawLayer::MoveArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, 1530 SCCOL nDx,SCROW nDy, bool bInsDel, bool bUpdateNoteCaptionPos ) 1531 { 1532 OSL_ENSURE( pDoc, "ScDrawLayer::MoveArea without document" ); 1533 if ( !pDoc ) 1534 return; 1535 1536 if (!bAdjustEnabled) 1537 return; 1538 1539 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1540 1541 tools::Rectangle aRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); 1542 lcl_ReverseTwipsToMM( aRect ); 1543 //TODO: use twips directly? 1544 1545 Point aMove; 1546 1547 if (nDx > 0) 1548 for (SCCOL s=0; s<nDx; s++) 1549 aMove.AdjustX(pDoc->GetColWidth(s+nCol1,nTab) ); 1550 else 1551 for (SCCOL s=-1; s>=nDx; s--) 1552 aMove.AdjustX( -(pDoc->GetColWidth(s+nCol1,nTab)) ); 1553 if (nDy > 0) 1554 aMove.AdjustY(pDoc->GetRowHeight( nRow1, nRow1+nDy-1, nTab) ); 1555 else 1556 aMove.AdjustY( -sal_Int16(pDoc->GetRowHeight( nRow1+nDy, nRow1-1, nTab)) ); 1557 1558 if ( bNegativePage ) 1559 aMove.setX( -aMove.X() ); 1560 1561 Point aTopLeft = aRect.TopLeft(); // Beginning when zoomed out 1562 if (bInsDel) 1563 { 1564 if ( aMove.X() != 0 && nDx < 0 ) // nDx counts cells, sign is independent of RTL 1565 aTopLeft.AdjustX(aMove.X() ); 1566 if ( aMove.Y() < 0 ) 1567 aTopLeft.AdjustY(aMove.Y() ); 1568 } 1569 1570 // Detectiv arrows: Adjust cell position 1571 1572 MoveCells( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy, bUpdateNoteCaptionPos ); 1573 } 1574 1575 bool ScDrawLayer::HasObjectsInRows( SCTAB nTab, SCROW nStartRow, SCROW nEndRow ) 1576 { 1577 OSL_ENSURE( pDoc, "ScDrawLayer::HasObjectsInRows without document" ); 1578 if ( !pDoc ) 1579 return false; 1580 1581 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1582 OSL_ENSURE(pPage,"Page not found"); 1583 if (!pPage) 1584 return false; 1585 1586 // for an empty page, there's no need to calculate the row heights 1587 if (!pPage->GetObjCount()) 1588 return false; 1589 1590 tools::Rectangle aTestRect; 1591 1592 aTestRect.AdjustTop(pDoc->GetRowHeight( 0, nStartRow-1, nTab) ); 1593 1594 if (nEndRow==pDoc->MaxRow()) 1595 aTestRect.SetBottom( MAXMM ); 1596 else 1597 { 1598 aTestRect.SetBottom( aTestRect.Top() ); 1599 aTestRect.AdjustBottom(pDoc->GetRowHeight( nStartRow, nEndRow, nTab) ); 1600 aTestRect.SetBottom(convertTwipToMm100(aTestRect.Bottom())); 1601 } 1602 1603 aTestRect.SetTop(convertTwipToMm100(aTestRect.Top())); 1604 1605 aTestRect.SetLeft( 0 ); 1606 aTestRect.SetRight( MAXMM ); 1607 1608 bool bNegativePage = pDoc->IsNegativePage( nTab ); 1609 if ( bNegativePage ) 1610 MirrorRectRTL( aTestRect ); 1611 1612 bool bFound = false; 1613 1614 tools::Rectangle aObjRect; 1615 SdrObjListIter aIter( pPage ); 1616 SdrObject* pObject = aIter.Next(); 1617 while ( pObject && !bFound ) 1618 { 1619 aObjRect = pObject->GetSnapRect(); //TODO: GetLogicRect ? 1620 if (aTestRect.Contains(aObjRect.TopLeft()) || aTestRect.Contains(aObjRect.BottomLeft())) 1621 bFound = true; 1622 1623 pObject = aIter.Next(); 1624 } 1625 1626 return bFound; 1627 } 1628 1629 void ScDrawLayer::DeleteObjectsInArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, 1630 SCCOL nCol2,SCROW nRow2, bool bAnchored ) 1631 { 1632 OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInArea without document" ); 1633 if ( !pDoc ) 1634 return; 1635 1636 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1637 OSL_ENSURE(pPage,"Page ?"); 1638 if (!pPage) 1639 return; 1640 1641 pPage->RecalcObjOrdNums(); 1642 1643 const size_t nObjCount = pPage->GetObjCount(); 1644 if (!nObjCount) 1645 return; 1646 1647 tools::Rectangle aDelRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); 1648 tools::Rectangle aDelCircle = aDelRect; 1649 aDelCircle.AdjustLeft(-250); 1650 aDelCircle.AdjustRight(250); 1651 aDelCircle.AdjustTop(-70); 1652 aDelCircle.AdjustBottom(70); 1653 1654 std::vector<SdrObject*> ppObj; 1655 ppObj.reserve(nObjCount); 1656 1657 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1658 SdrObject* pObject = aIter.Next(); 1659 while (pObject) 1660 { 1661 // do not delete note caption, they are always handled by the cell note 1662 // TODO: detective objects are still deleted, is this desired? 1663 if (!IsNoteCaption( pObject )) 1664 { 1665 tools::Rectangle aObjRect; 1666 ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject); 1667 if (pObjData && pObjData->meType == ScDrawObjData::ValidationCircle) 1668 { 1669 aObjRect = pObject->GetLogicRect(); 1670 if(aDelCircle.Contains(aObjRect)) 1671 ppObj.push_back(pObject); 1672 } 1673 else 1674 { 1675 aObjRect = pObject->GetCurrentBoundRect(); 1676 if (aDelRect.Contains(aObjRect)) 1677 { 1678 if (bAnchored) 1679 { 1680 ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject); 1681 if (aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE) 1682 ppObj.push_back(pObject); 1683 } 1684 else 1685 ppObj.push_back(pObject); 1686 } 1687 } 1688 } 1689 1690 pObject = aIter.Next(); 1691 } 1692 1693 if (bRecording) 1694 for (auto p : ppObj) 1695 AddCalcUndo(std::make_unique<SdrUndoDelObj>(*p)); 1696 1697 for (auto p : ppObj) 1698 pPage->RemoveObject(p->GetOrdNum()); 1699 } 1700 1701 void ScDrawLayer::DeleteObjectsInSelection( const ScMarkData& rMark ) 1702 { 1703 OSL_ENSURE( pDoc, "ScDrawLayer::DeleteObjectsInSelection without document" ); 1704 if ( !pDoc ) 1705 return; 1706 1707 if ( !rMark.IsMultiMarked() ) 1708 return; 1709 1710 const ScRange& aMarkRange = rMark.GetMultiMarkArea(); 1711 1712 SCTAB nTabCount = pDoc->GetTableCount(); 1713 for (const SCTAB nTab : rMark) 1714 { 1715 if (nTab >= nTabCount) 1716 break; 1717 1718 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 1719 if (pPage) 1720 { 1721 pPage->RecalcObjOrdNums(); 1722 const size_t nObjCount = pPage->GetObjCount(); 1723 if (nObjCount) 1724 { 1725 // Rectangle around the whole selection 1726 tools::Rectangle aMarkBound = pDoc->GetMMRect( 1727 aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), 1728 aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab ); 1729 1730 std::vector<SdrObject*> ppObj; 1731 ppObj.reserve(nObjCount); 1732 1733 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 1734 SdrObject* pObject = aIter.Next(); 1735 while (pObject) 1736 { 1737 // do not delete note caption, they are always handled by the cell note 1738 // TODO: detective objects are still deleted, is this desired? 1739 if (!IsNoteCaption( pObject )) 1740 { 1741 tools::Rectangle aObjRect = pObject->GetCurrentBoundRect(); 1742 ScRange aRange = pDoc->GetRange(nTab, aObjRect); 1743 bool bObjectInMarkArea = 1744 aMarkBound.Contains(aObjRect) && rMark.IsAllMarked(aRange); 1745 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObject); 1746 ScAnchorType aAnchorType = ScDrawLayer::GetAnchorType(*pObject); 1747 bool bObjectAnchoredToMarkedCell 1748 = ((aAnchorType == SCA_CELL || aAnchorType == SCA_CELL_RESIZE) 1749 && pObjData && pObjData->maStart.IsValid() 1750 && rMark.IsCellMarked(pObjData->maStart.Col(), 1751 pObjData->maStart.Row())); 1752 if (bObjectInMarkArea || bObjectAnchoredToMarkedCell) 1753 { 1754 ppObj.push_back(pObject); 1755 } 1756 } 1757 1758 pObject = aIter.Next(); 1759 } 1760 1761 // Delete objects (backwards) 1762 1763 if (bRecording) 1764 for (auto p : ppObj) 1765 AddCalcUndo(std::make_unique<SdrUndoDelObj>(*p)); 1766 1767 for (auto p : ppObj) 1768 pPage->RemoveObject(p->GetOrdNum()); 1769 } 1770 } 1771 else 1772 { 1773 OSL_FAIL("pPage?"); 1774 } 1775 } 1776 } 1777 1778 void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const tools::Rectangle& rRange ) 1779 { 1780 // copy everything in the specified range into the same page (sheet) in the clipboard doc 1781 1782 SdrPage* pSrcPage = GetPage(static_cast<sal_uInt16>(nTab)); 1783 if (!pSrcPage) 1784 return; 1785 1786 ScDrawLayer* pDestModel = nullptr; 1787 SdrPage* pDestPage = nullptr; 1788 1789 ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nTab); 1790 1791 SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); 1792 SdrObject* pOldObject = aIter.Next(); 1793 while (pOldObject) 1794 { 1795 // Catch objects where the object itself is inside the rectangle to be copied. 1796 bool bObjectInArea = rRange.Contains(pOldObject->GetCurrentBoundRect()); 1797 // Catch objects whose anchor is inside the rectangle to be copied. 1798 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); 1799 if (pObjData) 1800 bObjectInArea = bObjectInArea || aClipRange.Contains(pObjData->maStart); 1801 1802 // do not copy internal objects (detective) and note captions 1803 if (bObjectInArea && pOldObject->GetLayer() != SC_LAYER_INTERN 1804 && !IsNoteCaption(pOldObject)) 1805 { 1806 if ( !pDestModel ) 1807 { 1808 pDestModel = pClipDoc->GetDrawLayer(); // does the document already have a drawing layer? 1809 if ( !pDestModel ) 1810 { 1811 // allocate drawing layer in clipboard document only if there are objects to copy 1812 1813 pClipDoc->InitDrawLayer(); //TODO: create contiguous pages 1814 pDestModel = pClipDoc->GetDrawLayer(); 1815 } 1816 if (pDestModel) 1817 pDestPage = pDestModel->GetPage( static_cast<sal_uInt16>(nTab) ); 1818 } 1819 1820 OSL_ENSURE( pDestPage, "no page" ); 1821 if (pDestPage) 1822 { 1823 // Clone to target SdrModel 1824 rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*pDestModel)); 1825 uno::Reference< chart2::XChartDocument > xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject ) ); 1826 if(!xOldChart.is())//#i110034# do not move charts as they lose all their data references otherwise 1827 { 1828 if (pObjData) 1829 { 1830 // The object is anchored to cell. The position is determined by the start 1831 // address. Copying into the clipboard does not change the anchor. 1832 // ToDo: Adapt Offset relative to anchor cell size for cell anchored. 1833 // ToDo: Adapt Offset and size for cell-anchored with resize objects. 1834 // ToDo: Exclude object from resize if disallowed at object. 1835 } 1836 else 1837 { 1838 // The object is anchored to page. We make its position so, that the 1839 // cell behind the object will have the same address in clipboard document as 1840 // in source document. So we will be able to reconstruct the original cell 1841 // address from position when pasting the object. 1842 tools::Rectangle aObjRect = pOldObject->GetSnapRect(); 1843 ScRange aPseudoAnchor 1844 = pDoc->GetRange(nTab, aObjRect, true /*bHiddenAsZero*/); 1845 tools::Rectangle aSourceCellRect 1846 = GetCellRect(*pDoc, aPseudoAnchor.aStart, false /*bMergedCell*/); 1847 tools::Rectangle aDestCellRect 1848 = GetCellRect(*pClipDoc, aPseudoAnchor.aStart, false); 1849 Point aMove = aDestCellRect.TopLeft() - aSourceCellRect.TopLeft(); 1850 pNewObject->NbcMove(Size(aMove.getX(), aMove.getY())); 1851 } 1852 } 1853 1854 pDestPage->InsertObject( pNewObject.get() ); 1855 1856 // no undo needed in clipboard document 1857 // charts are not updated 1858 } 1859 } 1860 1861 pOldObject = aIter.Next(); 1862 } 1863 } 1864 1865 static bool lcl_IsAllInRange( const ::std::vector< ScRangeList >& rRangesVector, const ScRange& rClipRange ) 1866 { 1867 // check if every range of rRangesVector is completely in rClipRange 1868 1869 for( const ScRangeList& rRanges : rRangesVector ) 1870 { 1871 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) 1872 { 1873 const ScRange & rRange = rRanges[ i ]; 1874 if ( !rClipRange.Contains( rRange ) ) 1875 { 1876 return false; // at least one range is not valid 1877 } 1878 } 1879 } 1880 1881 return true; // everything is fine 1882 } 1883 1884 static bool lcl_MoveRanges( ::std::vector< ScRangeList >& rRangesVector, const ScRange& rSourceRange, 1885 const ScAddress& rDestPos, const ScDocument& rDoc ) 1886 { 1887 bool bChanged = false; 1888 1889 ScRange aErrorRange( ScAddress::UNINITIALIZED ); 1890 for( ScRangeList& rRanges : rRangesVector ) 1891 { 1892 for ( size_t i = 0, nCount = rRanges.size(); i < nCount; i++ ) 1893 { 1894 ScRange & rRange = rRanges[ i ]; 1895 if ( rSourceRange.Contains( rRange ) ) 1896 { 1897 SCCOL nDiffX = rDestPos.Col() - rSourceRange.aStart.Col(); 1898 SCROW nDiffY = rDestPos.Row() - rSourceRange.aStart.Row(); 1899 SCTAB nDiffZ = rDestPos.Tab() - rSourceRange.aStart.Tab(); 1900 if (!rRange.Move( nDiffX, nDiffY, nDiffZ, aErrorRange, rDoc )) 1901 { 1902 assert(!"can't move range"); 1903 } 1904 bChanged = true; 1905 } 1906 } 1907 } 1908 1909 return bChanged; 1910 } 1911 1912 void ScDrawLayer::CopyFromClip(ScDrawLayer* pClipModel, SCTAB nSourceTab, 1913 const ScRange& rSourceRange, const ScAddress& rDestPos, 1914 const ScRange& rDestRange, bool bTransposing) 1915 { 1916 OSL_ENSURE( pDoc, "ScDrawLayer::CopyFromClip without document" ); 1917 if ( !pDoc ) 1918 return; 1919 1920 if (!pClipModel) 1921 return; 1922 1923 if (bDrawIsInUndo) //TODO: can this happen? 1924 { 1925 OSL_FAIL("CopyFromClip, bDrawIsInUndo"); 1926 return; 1927 } 1928 1929 SCTAB nDestTab = rDestPos.Tab(); 1930 1931 SdrPage* pSrcPage = pClipModel->GetPage(static_cast<sal_uInt16>(nSourceTab)); 1932 SdrPage* pDestPage = GetPage(static_cast<sal_uInt16>(nDestTab)); 1933 OSL_ENSURE( pSrcPage && pDestPage, "draw page missing" ); 1934 if ( !pSrcPage || !pDestPage ) 1935 return; 1936 1937 ScDocument* pClipDoc = pClipModel->GetDocument(); 1938 if (!pClipDoc) 1939 return; // Can this happen? And if yes, what to do? 1940 1941 SdrObjListIter aIter( pSrcPage, SdrIterMode::Flat ); 1942 SdrObject* pOldObject = aIter.Next(); 1943 if (!pOldObject) 1944 return; // no objects at all. Nothing to do. 1945 1946 // a clipboard document and its source share the same document item pool, 1947 // so the pointers can be compared to see if this is copy&paste within 1948 // the same document 1949 bool bSameDoc = pDoc->GetPool() == pClipDoc->GetPool(); 1950 bool bDestClip = pDoc->IsClipboard(); // Happens while transposing. ToDo: Other cases? 1951 1952 //#i110034# charts need correct sheet names for xml range conversion during load 1953 //so the target sheet name is temporarily renamed (if we have any SdrObjects) 1954 OUString aDestTabName; 1955 bool bRestoreDestTabName = false; 1956 if (!bSameDoc && !bDestClip) 1957 { 1958 OUString aSourceTabName; 1959 if (pClipDoc->GetName(nSourceTab, aSourceTabName) && pDoc->GetName(nDestTab, aDestTabName) 1960 && aSourceTabName != aDestTabName && pDoc->ValidNewTabName(aSourceTabName)) 1961 { 1962 bRestoreDestTabName = pDoc->RenameTab( nDestTab, aSourceTabName ); 1963 } 1964 } 1965 1966 SCTAB nClipTab = bRestoreDestTabName ? nDestTab : nSourceTab; 1967 ScRange aClipRange = lcl_getClipRangeFromClipDoc(pClipDoc, nClipTab); 1968 1969 // We are going to make all rectangle calculations on LTR, so determine whether doc is RTL. 1970 bool bSourceRTL = pClipDoc->IsLayoutRTL(nSourceTab); 1971 bool bDestRTL = pDoc->IsLayoutRTL(nDestTab); 1972 1973 while (pOldObject) 1974 { 1975 // ToDO: Can this happen? Such objects should not be in the clipboard document. 1976 // do not copy internal objects (detective) and note captions 1977 if ((pOldObject->GetLayer() == SC_LAYER_INTERN) || IsNoteCaption(pOldObject)) 1978 { 1979 pOldObject = aIter.Next(); 1980 continue; 1981 } 1982 1983 // 'aIter' considers all objects on pSrcPage. But ScDocument::CopyBlockFromClip, which is used 1984 // for filtered data, acts not on the total range but only on parts of it. So we need to look, 1985 // whether an object is really contained in the current rSourceRange. 1986 // For cell anchored objects we use the start address of the anchor, for page anchored objects 1987 // we use the cell range behind the bounding box of the shape. 1988 ScAddress aSrcObjStart; 1989 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pOldObject); 1990 if (pObjData) // Object is anchored to cell. 1991 { 1992 aSrcObjStart = (*pObjData).maStart; 1993 } 1994 else // Object is anchored to page. 1995 { 1996 aSrcObjStart = pClipDoc->GetRange(nClipTab, pOldObject->GetCurrentBoundRect()).aStart; 1997 } 1998 if (!rSourceRange.Contains(aSrcObjStart)) 1999 { 2000 pOldObject = aIter.Next(); 2001 continue; 2002 } 2003 // If object is anchored to a filtered cell, we will not copy it, because filtered rows are 2004 // eliminated in paste. Copying would produce hidden objects which can only be accessed per 2005 // macro. 2006 if (pObjData && pClipDoc->RowFiltered((*pObjData).maStart.Row(), nSourceTab)) 2007 { 2008 pOldObject = aIter.Next(); 2009 continue; 2010 } 2011 2012 // Copy style sheet 2013 auto pStyleSheet = pOldObject->GetStyleSheet(); 2014 if (pStyleSheet && !bSameDoc) 2015 pDoc->GetStyleSheetPool()->CopyStyleFrom(pClipModel->GetStyleSheetPool(), 2016 pStyleSheet->GetName(), 2017 pStyleSheet->GetFamily(), true); 2018 2019 rtl::Reference<SdrObject> pNewObject(pOldObject->CloneSdrObject(*this)); 2020 tools::Rectangle aObjRect = pOldObject->GetSnapRect(); 2021 if (bSourceRTL) 2022 { 2023 MirrorRTL(pNewObject.get()); // We make the calculations in LTR. 2024 MirrorRectRTL(aObjRect); 2025 } 2026 2027 bool bCanResize = IsResizeWithCell(*pOldObject) && !pOldObject->IsResizeProtect(); 2028 // We do not resize charts or other OLE objects and do not resize when transposing. 2029 bCanResize &= pOldObject->GetObjIdentifier() != SdrObjKind::OLE2; 2030 bCanResize &= !bTransposing && !pClipDoc->GetClipParam().isTransposed(); 2031 if (bCanResize) 2032 { 2033 // Filtered rows are eliminated on paste. Filtered cols do not exist as of May 2023. 2034 // Collapsed or hidden rows/cols are shown on paste. 2035 // Idea: First calculate top left cell and bottom right cell of pasted object. Then 2036 // calculate the object corners inside these cell and from that build new snap rectangle. 2037 // We assume that pObjData is valid and pObjData and aObjRect correspond to each other 2038 // in the source document. 2039 2040 // Start cell of object in source and destination. The case of a filtered start cell is 2041 // already excluded above. aSrcObjStart = (*pObjData).maStart is already done above. 2042 // If filtered rows exist in the total range, the range was divided into blocks which 2043 // do not contain filtered rows. So the rows between start of aSourceRange and object 2044 // start row do not contain filtered rows. 2045 SCROW nStartRowDiff = aSrcObjStart.Row() - rSourceRange.aStart.Row(); 2046 SCCOL nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col(); 2047 ScAddress aDestObjStart = rDestRange.aStart; 2048 aDestObjStart.IncCol(nStartColDiff); 2049 aDestObjStart.IncRow(nStartRowDiff); 2050 2051 // End cell of object in source and destination. We look at the amount of rows/cols to be 2052 // added to get object end cell from object start cell. 2053 ScAddress aSrcObjEnd = (*pObjData).maEnd; 2054 SCCOL nColsToAdd = aSrcObjEnd.Col() - aSrcObjStart.Col(); 2055 SCROW nRowsToAdd 2056 = pClipDoc->CountNonFilteredRows(aSrcObjStart.Row(), aSrcObjEnd.Row(), nSourceTab) 2057 - 1; 2058 ScAddress aDestObjEnd = aDestObjStart; 2059 aDestObjEnd.IncCol(nColsToAdd); 2060 aDestObjEnd.IncRow(nRowsToAdd); 2061 2062 // Position of object inside start and end cell in source. We describe the distance from 2063 // cell corner to object corner as ratio of offset to cell width/height. 2064 // We cannot use GetCellRect method, because that uses bHiddenAsZero=true. 2065 Point aSrcObjTopLeftOffset = (*pObjData).maStartOffset; 2066 tools::Rectangle aSrcStartRect 2067 = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(), 2068 aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/); 2069 if (bSourceRTL) 2070 MirrorRectRTL(aSrcStartRect); 2071 double fStartXRatio 2072 = aSrcStartRect.getOpenWidth() == 0 2073 ? 1.0 2074 : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth()); 2075 double fStartYRatio 2076 = aSrcStartRect.getOpenHeight() == 0 2077 ? 1.0 2078 : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight()); 2079 2080 Point aSrcObjBottomRightOffset = (*pObjData).maEndOffset; 2081 tools::Rectangle aSrcEndRect 2082 = pClipDoc->GetMMRect(aSrcObjEnd.Col(), aSrcObjEnd.Row(), aSrcObjEnd.Col(), 2083 aSrcObjEnd.Row(), nSourceTab, false /*bHiddenAsZero*/); 2084 if (bSourceRTL) 2085 MirrorRectRTL(aSrcEndRect); 2086 double fEndXRatio 2087 = aSrcEndRect.getOpenWidth() == 0 2088 ? 1.0 2089 : double(aSrcObjBottomRightOffset.X()) / double(aSrcEndRect.getOpenWidth()); 2090 double fEndYRatio 2091 = aSrcEndRect.getOpenHeight() == 0 2092 ? 1.0 2093 : double(aSrcObjBottomRightOffset.Y()) / double(aSrcEndRect.getOpenHeight()); 2094 // The end cell given in pObjData might be filtered. In that case the object is cut at 2095 // the lower cell edge. The offset is as large as the cell. 2096 if (pClipDoc->RowFiltered(aSrcObjEnd.Row(), nSourceTab)) 2097 fEndYRatio = 1.0; 2098 2099 // Position of object inside start and end cell in destination 2100 tools::Rectangle aDestStartRect 2101 = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/); 2102 if (bDestRTL) 2103 MirrorRectRTL(aDestStartRect); 2104 Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(), 2105 fStartYRatio * aDestStartRect.getOpenHeight()); 2106 Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset; 2107 2108 tools::Rectangle aDestEndRect = GetCellRect(*pDoc, aDestObjEnd, false /*bMergedCell*/); 2109 if (bDestRTL) 2110 MirrorRectRTL(aDestEndRect); 2111 Point aDestObjBottomRightOffset(fEndXRatio * aDestEndRect.getOpenWidth(), 2112 fEndYRatio * aDestEndRect.getOpenHeight()); 2113 Point aDestObjBottomRight = aDestEndRect.TopLeft() + aDestObjBottomRightOffset; 2114 2115 // Fit new object into destination rectangle 2116 tools::Rectangle aNewObjRect(aDestObjTopLeft, aDestObjBottomRight); 2117 aNewObjRect = lcl_makeSafeRectangle(aNewObjRect); 2118 if (pNewObject->GetObjIdentifier() == SdrObjKind::CustomShape) 2119 pNewObject->AdjustToMaxRect(aNewObjRect); 2120 else 2121 pNewObject->SetSnapRect(aNewObjRect); 2122 } 2123 else 2124 { 2125 // We determine the MM-distance of the new object from its start cell in destination from 2126 // the ratio of offset to cell width/height. Thus the object still starts in this cell 2127 // even if the destination cell has different size. Otherwise we might lose objects when 2128 // transposing. 2129 2130 // Start Cell address in source and destination 2131 SCCOLROW nStartRowDiff = pClipDoc->CountNonFilteredRows(rSourceRange.aStart.Row(), 2132 aSrcObjStart.Row(), nSourceTab) 2133 - 1; 2134 SCCOLROW nStartColDiff = aSrcObjStart.Col() - rSourceRange.aStart.Col(); 2135 if (bTransposing) 2136 std::swap(nStartRowDiff, nStartColDiff); 2137 ScAddress aDestObjStart = rDestRange.aStart; 2138 aDestObjStart.IncCol(nStartColDiff); 2139 aDestObjStart.IncRow(nStartRowDiff); 2140 2141 // Position of object inside start cell in source. 2142 tools::Rectangle aSrcStartRect 2143 = pClipDoc->GetMMRect(aSrcObjStart.Col(), aSrcObjStart.Row(), aSrcObjStart.Col(), 2144 aSrcObjStart.Row(), nSourceTab, false /*bHiddenAsZero*/); 2145 if (bSourceRTL) 2146 MirrorRectRTL(aSrcStartRect); 2147 Point aSrcObjTopLeftOffset = pObjData ? (*pObjData).maStartOffset 2148 : aObjRect.TopLeft() - aSrcStartRect.TopLeft(); 2149 2150 double fStartXRatio 2151 = aSrcStartRect.getOpenWidth() == 0 2152 ? 1.0 2153 : double(aSrcObjTopLeftOffset.X()) / double(aSrcStartRect.getOpenWidth()); 2154 double fStartYRatio 2155 = aSrcStartRect.getOpenHeight() == 0 2156 ? 1.0 2157 : double(aSrcObjTopLeftOffset.Y()) / double(aSrcStartRect.getOpenHeight()); 2158 2159 // Position of object inside start cell in destination 2160 tools::Rectangle aDestStartRect 2161 = GetCellRect(*pDoc, aDestObjStart, false /*bMergedCell*/); 2162 if (bDestRTL) 2163 MirrorRectRTL(aDestStartRect); 2164 Point aDestObjTopLeftOffset(fStartXRatio * aDestStartRect.getOpenWidth(), 2165 fStartYRatio * aDestStartRect.getOpenHeight()); 2166 Point aDestObjTopLeft = aDestStartRect.TopLeft() + aDestObjTopLeftOffset; 2167 2168 // Move new object to new position 2169 Point aMoveBy = aDestObjTopLeft - aObjRect.TopLeft(); 2170 pNewObject->NbcMove(Size(aMoveBy.getX(), aMoveBy.getY())); 2171 } 2172 2173 if (bDestRTL) 2174 MirrorRTL(pNewObject.get()); 2175 2176 // Changing object position or size does not automatically change its anchor. 2177 if (IsCellAnchored(*pOldObject)) 2178 SetCellAnchoredFromPosition(*pNewObject, *pDoc, nDestTab, 2179 IsResizeWithCell(*pOldObject)); 2180 2181 // InsertObject includes broadcasts 2182 // MakeNameUnique makes the pasted objects accessible via Navigator. 2183 if (bDestClip) 2184 pDestPage->InsertObject(pNewObject.get()); 2185 else 2186 { 2187 if (bRecording) 2188 pDoc->EnableUndo(false); 2189 pDestPage->InsertObjectThenMakeNameUnique(pNewObject.get()); 2190 if (bRecording) 2191 pDoc->EnableUndo(true); 2192 } 2193 2194 if (bRecording) 2195 AddCalcUndo(std::make_unique<SdrUndoInsertObj>(*pNewObject)); 2196 2197 //#i110034# handle chart data references (after InsertObject) 2198 if (pNewObject->GetObjIdentifier() == SdrObjKind::OLE2) 2199 { 2200 uno::Reference<embed::XEmbeddedObject> xIPObj 2201 = static_cast<SdrOle2Obj*>(pNewObject.get())->GetObjRef(); 2202 uno::Reference<embed::XClassifiedObject> xClassified = xIPObj; 2203 SvGlobalName aObjectClassName; 2204 if (xClassified.is()) 2205 { 2206 try 2207 { 2208 aObjectClassName = SvGlobalName(xClassified->getClassID()); 2209 } 2210 catch (uno::Exception&) 2211 { 2212 // TODO: handle error? 2213 } 2214 } 2215 2216 if (xIPObj.is() && SotExchange::IsChart(aObjectClassName)) 2217 { 2218 uno::Reference<chart2::XChartDocument> xNewChart( 2219 ScChartHelper::GetChartFromSdrObject(pNewObject.get())); 2220 if (xNewChart.is() && !xNewChart->hasInternalDataProvider()) 2221 { 2222 OUString aChartName 2223 = static_cast<SdrOle2Obj*>(pNewObject.get())->GetPersistName(); 2224 ::std::vector<ScRangeList> aRangesVector; 2225 pDoc->GetChartRanges(aChartName, aRangesVector, *pDoc); 2226 if (!aRangesVector.empty()) 2227 { 2228 bool bInSourceRange = false; 2229 bInSourceRange = lcl_IsAllInRange(aRangesVector, aClipRange); 2230 2231 // always lose references when pasting into a clipboard document (transpose) 2232 if ((bInSourceRange || bSameDoc) && !bDestClip) 2233 { 2234 if (bInSourceRange) 2235 { 2236 if (rDestPos != aClipRange.aStart) 2237 { 2238 // update the data ranges to the new (copied) position 2239 if (lcl_MoveRanges(aRangesVector, aClipRange, rDestPos, *pDoc)) 2240 pDoc->SetChartRanges(aChartName, aRangesVector); 2241 } 2242 } 2243 else 2244 { 2245 // leave the ranges unchanged 2246 } 2247 } 2248 else 2249 { 2250 // pasting into a new document without the complete source data 2251 // -> break connection to source data and switch to own data 2252 uno::Reference<chart::XChartDocument> xOldChartDoc( 2253 ScChartHelper::GetChartFromSdrObject(pOldObject), uno::UNO_QUERY); 2254 uno::Reference<chart::XChartDocument> xNewChartDoc(xNewChart, 2255 uno::UNO_QUERY); 2256 if (xOldChartDoc.is() && xNewChartDoc.is()) 2257 xNewChartDoc->attachData(xOldChartDoc->getData()); 2258 2259 // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc) 2260 } 2261 } 2262 } 2263 } 2264 } 2265 pOldObject = aIter.Next(); 2266 } 2267 2268 if( bRestoreDestTabName ) 2269 pDoc->RenameTab( nDestTab, aDestTabName ); 2270 } 2271 2272 void ScDrawLayer::MirrorRTL( SdrObject* pObj ) 2273 { 2274 OSL_ENSURE( pDoc, "ScDrawLayer::MirrorRTL - missing document" ); 2275 if( !pDoc ) 2276 return; 2277 2278 SdrObjKind nIdent = pObj->GetObjIdentifier(); 2279 2280 // don't mirror OLE or graphics, otherwise ask the object 2281 // if it can be mirrored 2282 bool bCanMirror = ( nIdent != SdrObjKind::Graphic && nIdent != SdrObjKind::OLE2 ); 2283 if (bCanMirror) 2284 { 2285 SdrObjTransformInfoRec aInfo; 2286 pObj->TakeObjInfo( aInfo ); 2287 bCanMirror = aInfo.bMirror90Allowed; 2288 } 2289 2290 if (bCanMirror) 2291 { 2292 ScDrawObjData* pData = GetObjData(pObj); 2293 if (pData) // cell anchored 2294 { 2295 // Remember values from positive side. 2296 const tools::Rectangle aOldSnapRect = pObj->GetSnapRect(); 2297 const tools::Rectangle aOldLogicRect = pObj->GetLogicRect(); 2298 // Generate noRotate anchor if missing. 2299 ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj); 2300 if (!pNoRotatedAnchor) 2301 { 2302 ScDrawObjData aNoRotateAnchor; 2303 const tools::Rectangle aLogicRect(pObj->GetLogicRect()); 2304 GetCellAnchorFromPosition(aLogicRect, aNoRotateAnchor, 2305 *pDoc, pData->maStart.Tab()); 2306 aNoRotateAnchor.mbResizeWithCell = pData->mbResizeWithCell; 2307 SetNonRotatedAnchor(*pObj, aNoRotateAnchor); 2308 pNoRotatedAnchor = GetNonRotatedObjData(pObj); 2309 assert(pNoRotatedAnchor); 2310 } 2311 // Mirror object at vertical axis 2312 Point aRef1( 0, 0 ); 2313 Point aRef2( 0, 1 ); 2314 if (bRecording) 2315 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 2316 pObj->Mirror( aRef1, aRef2 ); 2317 2318 // Adapt offsets in pNoRotatedAnchor so, that object will be moved to current position in 2319 // save and reload. 2320 const tools::Long nInverseShift = aOldSnapRect.Left() + aOldSnapRect.Right(); 2321 const Point aLogicLT = pObj->GetLogicRect().TopLeft(); 2322 const Point aMirroredLogicLT = aLogicLT + Point(nInverseShift, 0); 2323 const Point aOffsetDiff = aMirroredLogicLT - aOldLogicRect.TopLeft(); 2324 // new Offsets 2325 pNoRotatedAnchor->maStartOffset += aOffsetDiff; 2326 pNoRotatedAnchor->maEndOffset += aOffsetDiff; 2327 } 2328 else // page anchored 2329 { 2330 Point aRef1( 0, 0 ); 2331 Point aRef2( 0, 1 ); 2332 if (bRecording) 2333 AddCalcUndo( std::make_unique<SdrUndoGeoObj>( *pObj ) ); 2334 pObj->Mirror( aRef1, aRef2 ); 2335 } 2336 } 2337 else 2338 { 2339 // Move instead of mirroring: 2340 // New start position is negative of old end position 2341 // -> move by sum of start and end position 2342 tools::Rectangle aObjRect = pObj->GetSnapRect(); 2343 Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); 2344 if (bRecording) 2345 AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) ); 2346 pObj->Move( aMoveSize ); 2347 } 2348 2349 // for cell anchored objects adapt rectangles in anchors 2350 ScDrawObjData* pData = GetObjData(pObj); 2351 if (pData) 2352 { 2353 pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible()); 2354 ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/); 2355 pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible()); 2356 } 2357 } 2358 2359 void ScDrawLayer::MoveRTL(SdrObject* pObj) 2360 { 2361 tools::Rectangle aObjRect = pObj->GetSnapRect(); 2362 Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); 2363 if (bRecording) 2364 AddCalcUndo( std::make_unique<SdrUndoMoveObj>( *pObj, aMoveSize ) ); 2365 pObj->Move( aMoveSize ); 2366 2367 // for cell anchored objects adapt rectangles in anchors 2368 ScDrawObjData* pData = GetObjData(pObj); 2369 if (pData) 2370 { 2371 pData->setShapeRect(GetDocument(), pObj->GetSnapRect(), pObj->IsVisible()); 2372 ScDrawObjData* pNoRotatedAnchor = GetNonRotatedObjData(pObj, true /*bCreate*/); 2373 pNoRotatedAnchor->setShapeRect(GetDocument(), pObj->GetLogicRect(), pObj->IsVisible()); 2374 } 2375 } 2376 2377 void ScDrawLayer::MirrorRectRTL( tools::Rectangle& rRect ) 2378 { 2379 // mirror and swap left/right 2380 tools::Long nTemp = rRect.Left(); 2381 rRect.SetLeft( -rRect.Right() ); 2382 rRect.SetRight( -nTemp ); 2383 } 2384 2385 tools::Rectangle ScDrawLayer::GetCellRect( const ScDocument& rDoc, const ScAddress& rPos, bool bMergedCell ) 2386 { 2387 tools::Rectangle aCellRect; 2388 OSL_ENSURE( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ), "ScDrawLayer::GetCellRect - invalid cell address" ); 2389 if( rDoc.ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ) ) 2390 { 2391 // find top left position of passed cell address 2392 Point aTopLeft; 2393 for( SCCOL nCol = 0; nCol < rPos.Col(); ++nCol ) 2394 aTopLeft.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) ); 2395 if( rPos.Row() > 0 ) 2396 aTopLeft.AdjustY(rDoc.GetRowHeight( 0, rPos.Row() - 1, rPos.Tab() ) ); 2397 2398 // find bottom-right position of passed cell address 2399 ScAddress aEndPos = rPos; 2400 if( bMergedCell ) 2401 { 2402 const ScMergeAttr* pMerge = rDoc.GetAttr( rPos, ATTR_MERGE ); 2403 if( pMerge->GetColMerge() > 1 ) 2404 aEndPos.IncCol( pMerge->GetColMerge() - 1 ); 2405 if( pMerge->GetRowMerge() > 1 ) 2406 aEndPos.IncRow( pMerge->GetRowMerge() - 1 ); 2407 } 2408 Point aBotRight = aTopLeft; 2409 for( SCCOL nCol = rPos.Col(); nCol <= aEndPos.Col(); ++nCol ) 2410 aBotRight.AdjustX(rDoc.GetColWidth( nCol, rPos.Tab() ) ); 2411 aBotRight.AdjustY(rDoc.GetRowHeight( rPos.Row(), aEndPos.Row(), rPos.Tab() ) ); 2412 2413 // twips -> 1/100 mm 2414 aTopLeft = o3tl::convert(aTopLeft, o3tl::Length::twip, o3tl::Length::mm100); 2415 aBotRight = o3tl::convert(aBotRight, o3tl::Length::twip, o3tl::Length::mm100); 2416 2417 aCellRect = tools::Rectangle( aTopLeft, aBotRight ); 2418 if( rDoc.IsNegativePage( rPos.Tab() ) ) 2419 MirrorRectRTL( aCellRect ); 2420 } 2421 return aCellRect; 2422 } 2423 2424 OUString ScDrawLayer::GetVisibleName( const SdrObject* pObj ) 2425 { 2426 OUString aName = pObj->GetName(); 2427 if ( pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) 2428 { 2429 // For OLE, the user defined name (GetName) is used 2430 // if it's not empty (accepting possibly duplicate names), 2431 // otherwise the persist name is used so every object appears 2432 // in the Navigator at all. 2433 2434 if ( aName.isEmpty() ) 2435 aName = static_cast<const SdrOle2Obj*>(pObj)->GetPersistName(); 2436 } 2437 return aName; 2438 } 2439 2440 static bool IsNamedObject( const SdrObject* pObj, std::u16string_view rName ) 2441 { 2442 // sal_True if rName is the object's Name or PersistName 2443 // (used to find a named object) 2444 2445 return ( pObj->GetName() == rName || 2446 ( pObj->GetObjIdentifier() == SdrObjKind::OLE2 && 2447 static_cast<const SdrOle2Obj*>(pObj)->GetPersistName() == rName ) ); 2448 } 2449 2450 SdrObject* ScDrawLayer::GetNamedObject( std::u16string_view rName, SdrObjKind nId, SCTAB& rFoundTab ) const 2451 { 2452 sal_uInt16 nTabCount = GetPageCount(); 2453 for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) 2454 { 2455 const SdrPage* pPage = GetPage(nTab); 2456 OSL_ENSURE(pPage,"Page ?"); 2457 if (pPage) 2458 { 2459 SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); 2460 SdrObject* pObject = aIter.Next(); 2461 while (pObject) 2462 { 2463 if ( nId == SdrObjKind::NONE || pObject->GetObjIdentifier() == nId ) 2464 if ( IsNamedObject( pObject, rName ) ) 2465 { 2466 rFoundTab = static_cast<SCTAB>(nTab); 2467 return pObject; 2468 } 2469 2470 pObject = aIter.Next(); 2471 } 2472 } 2473 } 2474 2475 return nullptr; 2476 } 2477 2478 OUString ScDrawLayer::GetNewGraphicName( tools::Long* pnCounter ) const 2479 { 2480 OUString aBase = ScResId(STR_GRAPHICNAME) + " "; 2481 2482 bool bThere = true; 2483 OUString aGraphicName; 2484 SCTAB nDummy; 2485 tools::Long nId = pnCounter ? *pnCounter : 0; 2486 while (bThere) 2487 { 2488 ++nId; 2489 aGraphicName = aBase + OUString::number( nId ); 2490 bThere = ( GetNamedObject( aGraphicName, SdrObjKind::NONE, nDummy ) != nullptr ); 2491 } 2492 2493 if ( pnCounter ) 2494 *pnCounter = nId; 2495 2496 return aGraphicName; 2497 } 2498 2499 void ScDrawLayer::EnsureGraphicNames() 2500 { 2501 // make sure all graphic objects have names (after Excel import etc.) 2502 2503 sal_uInt16 nTabCount = GetPageCount(); 2504 for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) 2505 { 2506 SdrPage* pPage = GetPage(nTab); 2507 OSL_ENSURE(pPage,"Page ?"); 2508 if (pPage) 2509 { 2510 SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); 2511 SdrObject* pObject = aIter.Next(); 2512 2513 /* The index passed to GetNewGraphicName() will be set to 2514 the used index in each call. This prevents the repeated search 2515 for all names from 1 to current index. */ 2516 tools::Long nCounter = 0; 2517 2518 while (pObject) 2519 { 2520 if ( pObject->GetObjIdentifier() == SdrObjKind::Graphic && pObject->GetName().isEmpty()) 2521 pObject->SetName( GetNewGraphicName( &nCounter ) ); 2522 2523 pObject = aIter.Next(); 2524 } 2525 } 2526 } 2527 } 2528 2529 namespace 2530 { 2531 SdrObjUserData* GetFirstUserDataOfType(const SdrObject *pObj, sal_uInt16 nId) 2532 { 2533 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 2534 for( sal_uInt16 i = 0; i < nCount; i++ ) 2535 { 2536 SdrObjUserData* pData = pObj->GetUserData( i ); 2537 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId ) 2538 return pData; 2539 } 2540 return nullptr; 2541 } 2542 2543 void DeleteFirstUserDataOfType(SdrObject *pObj, sal_uInt16 nId) 2544 { 2545 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 2546 for( sal_uInt16 i = nCount; i > 0; i-- ) 2547 { 2548 SdrObjUserData* pData = pObj->GetUserData( i-1 ); 2549 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == nId ) 2550 pObj->DeleteUserData(i-1); 2551 } 2552 } 2553 } 2554 2555 void ScDrawLayer::SetNonRotatedAnchor(SdrObject& rObj, const ScDrawObjData& rAnchor) 2556 { 2557 ScDrawObjData* pAnchor = GetNonRotatedObjData( &rObj, true ); 2558 pAnchor->maStart = rAnchor.maStart; 2559 pAnchor->maEnd = rAnchor.maEnd; 2560 pAnchor->maStartOffset = rAnchor.maStartOffset; 2561 pAnchor->maEndOffset = rAnchor.maEndOffset; 2562 pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell; 2563 } 2564 2565 void ScDrawLayer::SetCellAnchored( SdrObject &rObj, const ScDrawObjData &rAnchor ) 2566 { 2567 ScDrawObjData* pAnchor = GetObjData( &rObj, true ); 2568 pAnchor->maStart = rAnchor.maStart; 2569 pAnchor->maEnd = rAnchor.maEnd; 2570 pAnchor->maStartOffset = rAnchor.maStartOffset; 2571 pAnchor->maEndOffset = rAnchor.maEndOffset; 2572 pAnchor->mbResizeWithCell = rAnchor.mbResizeWithCell; 2573 } 2574 2575 void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject &rObj, const ScDocument &rDoc, SCTAB nTab, 2576 bool bResizeWithCell ) 2577 { 2578 if (!rObj.IsVisible()) 2579 return; 2580 ScDrawObjData aAnchor; 2581 // set anchor in terms of the visual ( SnapRect ) 2582 // object ( e.g. for when object is rotated ) 2583 const tools::Rectangle aObjRect(rObj.GetSnapRect()); 2584 GetCellAnchorFromPosition( 2585 aObjRect, 2586 aAnchor, 2587 rDoc, 2588 nTab); 2589 2590 aAnchor.mbResizeWithCell = bResizeWithCell; 2591 SetCellAnchored( rObj, aAnchor ); 2592 2593 // absolutely necessary to set flag, ScDrawLayer::RecalcPos expects it. 2594 if ( ScDrawObjData* pAnchor = GetObjData( &rObj ) ) 2595 { 2596 pAnchor->setShapeRect(&rDoc, rObj.GetSnapRect()); 2597 } 2598 2599 // - keep also an anchor in terms of the Logic ( untransformed ) object 2600 // because that's what we stored ( and still do ) to xml 2601 2602 // Vertical flipped custom shapes need special treatment, see comment in 2603 // lcl_SetLogicRectFromAnchor 2604 tools::Rectangle aObjRect2; 2605 if (lcl_NeedsMirrorYCorrection(&rObj)) 2606 { 2607 const tools::Rectangle aRect(rObj.GetSnapRect()); 2608 const Point aLeft(aRect.Left(), (aRect.Top() + aRect.Bottom()) >> 1); 2609 const Point aRight(aLeft.X() + 1000, aLeft.Y()); 2610 rObj.NbcMirror(aLeft, aRight); 2611 aObjRect2 = rObj.GetLogicRect(); 2612 rObj.NbcMirror(aLeft, aRight); 2613 } 2614 else if (rObj.GetObjIdentifier() == SdrObjKind::Measure) 2615 { 2616 // tdf#137576. A SdrMeasureObj might have a wrong logic rect here. TakeUnrotatedSnapRect 2617 // calculates the current unrotated snap rectangle, sets logic rectangle and returns it. 2618 static_cast<SdrMeasureObj&>(rObj).TakeUnrotatedSnapRect(aObjRect2); 2619 } 2620 else 2621 aObjRect2 = rObj.GetLogicRect(); 2622 2623 // Values in XML are so as if it is a LTR sheet. The object is shifted to negative page on loading 2624 // so that the snap rectangle appears mirrored. For transformed objects the shifted logic rectangle 2625 // is not the mirrored LTR rectangle. We calculate the mirrored LTR rectangle here. 2626 if (rDoc.IsNegativePage(nTab)) 2627 { 2628 const tools::Rectangle aSnapRect(rObj.GetSnapRect()); 2629 aObjRect2.Move(Size(-aSnapRect.Left() - aSnapRect.Right(), 0)); 2630 MirrorRectRTL(aObjRect2); 2631 } 2632 2633 ScDrawObjData aNoRotatedAnchor; 2634 GetCellAnchorFromPosition( 2635 aObjRect2, 2636 aNoRotatedAnchor, 2637 rDoc, 2638 nTab); 2639 2640 aNoRotatedAnchor.mbResizeWithCell = bResizeWithCell; 2641 SetNonRotatedAnchor( rObj, aNoRotatedAnchor); 2642 // And update maShapeRect. It is used in adjustAnchoredPosition() in ScDrawView::Notify(). 2643 if (ScDrawObjData* pAnchor = GetNonRotatedObjData(&rObj)) 2644 { 2645 pAnchor->setShapeRect(&rDoc, rObj.GetLogicRect()); 2646 } 2647 } 2648 2649 void ScDrawLayer::GetCellAnchorFromPosition( 2650 const tools::Rectangle &rObjRect, 2651 ScDrawObjData &rAnchor, 2652 const ScDocument &rDoc, 2653 SCTAB nTab, 2654 bool bHiddenAsZero) 2655 { 2656 ScRange aRange = rDoc.GetRange( nTab, rObjRect, bHiddenAsZero ); 2657 2658 tools::Rectangle aCellRect; 2659 2660 rAnchor.maStart = aRange.aStart; 2661 aCellRect = rDoc.GetMMRect( aRange.aStart.Col(), aRange.aStart.Row(), 2662 aRange.aStart.Col(), aRange.aStart.Row(), aRange.aStart.Tab(), bHiddenAsZero ); 2663 rAnchor.maStartOffset.setY( rObjRect.Top()-aCellRect.Top() ); 2664 if (!rDoc.IsNegativePage(nTab)) 2665 rAnchor.maStartOffset.setX( rObjRect.Left()-aCellRect.Left() ); 2666 else 2667 rAnchor.maStartOffset.setX( aCellRect.Right()-rObjRect.Right() ); 2668 2669 rAnchor.maEnd = aRange.aEnd; 2670 aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(), 2671 aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab(), bHiddenAsZero ); 2672 if (!rObjRect.IsEmpty()) 2673 rAnchor.maEndOffset.setY( rObjRect.Bottom()-aCellRect.Top() ); 2674 if (!rDoc.IsNegativePage(nTab)) 2675 { 2676 if (!rObjRect.IsEmpty()) 2677 rAnchor.maEndOffset.setX( rObjRect.Right()-aCellRect.Left() ); 2678 } 2679 else 2680 rAnchor.maEndOffset.setX( aCellRect.Right()-rObjRect.Left() ); 2681 } 2682 2683 void ScDrawLayer::UpdateCellAnchorFromPositionEnd( const SdrObject &rObj, ScDrawObjData &rAnchor, const ScDocument &rDoc, SCTAB nTab, bool bUseLogicRect ) 2684 { 2685 tools::Rectangle aObjRect(bUseLogicRect ? rObj.GetLogicRect() : rObj.GetSnapRect()); 2686 ScRange aRange = rDoc.GetRange( nTab, aObjRect ); 2687 2688 ScDrawObjData* pAnchor = &rAnchor; 2689 pAnchor->maEnd = aRange.aEnd; 2690 2691 tools::Rectangle aCellRect = rDoc.GetMMRect( aRange.aEnd.Col(), aRange.aEnd.Row(), 2692 aRange.aEnd.Col(), aRange.aEnd.Row(), aRange.aEnd.Tab() ); 2693 pAnchor->maEndOffset.setY( aObjRect.Bottom()-aCellRect.Top() ); 2694 if (!rDoc.IsNegativePage(nTab)) 2695 pAnchor->maEndOffset.setX( aObjRect.Right()-aCellRect.Left() ); 2696 else 2697 pAnchor->maEndOffset.setX( aCellRect.Right()-aObjRect.Left() ); 2698 } 2699 2700 bool ScDrawLayer::IsCellAnchored( const SdrObject& rObj ) 2701 { 2702 // Cell anchored object always has a user data, to store the anchor cell 2703 // info. If it doesn't then it's page-anchored. 2704 return GetFirstUserDataOfType(&rObj, SC_UD_OBJDATA) != nullptr; 2705 } 2706 2707 bool ScDrawLayer::IsResizeWithCell( const SdrObject& rObj ) 2708 { 2709 // Cell anchored object always has a user data, to store the anchor cell 2710 // info. If it doesn't then it's page-anchored. 2711 ScDrawObjData* pDrawObjData = GetObjData(const_cast<SdrObject*>(&rObj)); 2712 if (!pDrawObjData) 2713 return false; 2714 2715 return pDrawObjData->mbResizeWithCell; 2716 } 2717 2718 void ScDrawLayer::SetPageAnchored( SdrObject &rObj ) 2719 { 2720 DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA); 2721 DeleteFirstUserDataOfType(&rObj, SC_UD_OBJDATA); 2722 } 2723 2724 ScAnchorType ScDrawLayer::GetAnchorType( const SdrObject &rObj ) 2725 { 2726 //If this object has a cell anchor associated with it 2727 //then it's cell-anchored, otherwise it's page-anchored 2728 const ScDrawObjData* pObjData = ScDrawLayer::GetObjData(const_cast<SdrObject*>(&rObj)); 2729 2730 // When there is no cell anchor, it is page anchored. 2731 if (!pObjData) 2732 return SCA_PAGE; 2733 2734 // It's cell-anchored, check if the object resizes with the cell 2735 if (pObjData->mbResizeWithCell) 2736 return SCA_CELL_RESIZE; 2737 2738 return SCA_CELL; 2739 } 2740 2741 std::vector<SdrObject*> 2742 ScDrawLayer::GetObjectsAnchoredToRows(SCTAB nTab, SCROW nStartRow, SCROW nEndRow) 2743 { 2744 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 2745 if (!pPage || pPage->GetObjCount() < 1) 2746 return std::vector<SdrObject*>(); 2747 2748 std::vector<SdrObject*> aObjects; 2749 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2750 SdrObject* pObject = aIter.Next(); 2751 ScRange aRange( 0, nStartRow, nTab, pDoc->MaxCol(), nEndRow, nTab); 2752 while (pObject) 2753 { 2754 ScDrawObjData* pObjData = GetObjData(pObject); 2755 if (pObjData && aRange.Contains(pObjData->maStart)) 2756 aObjects.push_back(pObject); 2757 pObject = aIter.Next(); 2758 } 2759 return aObjects; 2760 } 2761 2762 std::map<SCROW, std::vector<SdrObject*>> 2763 ScDrawLayer::GetObjectsAnchoredToRange(SCTAB nTab, SCCOL nCol, SCROW nStartRow, SCROW nEndRow) 2764 { 2765 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 2766 if (!pPage || pPage->GetObjCount() < 1) 2767 return std::map<SCROW, std::vector<SdrObject*>>(); 2768 2769 std::map<SCROW, std::vector<SdrObject*>> aRowObjects; 2770 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2771 SdrObject* pObject = aIter.Next(); 2772 ScRange aRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab); 2773 while (pObject) 2774 { 2775 if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently 2776 { 2777 ScDrawObjData* pObjData = GetObjData(pObject); 2778 if (pObjData && aRange.Contains(pObjData->maStart)) 2779 aRowObjects[pObjData->maStart.Row()].push_back(pObject); 2780 } 2781 pObject = aIter.Next(); 2782 } 2783 return aRowObjects; 2784 } 2785 2786 bool ScDrawLayer::HasObjectsAnchoredInRange(const ScRange& rRange) 2787 { 2788 // This only works for one table at a time 2789 assert(rRange.aStart.Tab() == rRange.aEnd.Tab()); 2790 2791 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(rRange.aStart.Tab())); 2792 if (!pPage || pPage->GetObjCount() < 1) 2793 return false; 2794 2795 SdrObjListIter aIter( pPage, SdrIterMode::Flat ); 2796 SdrObject* pObject = aIter.Next(); 2797 while (pObject) 2798 { 2799 if (!dynamic_cast<SdrCaptionObj*>(pObject)) // Caption objects are handled differently 2800 { 2801 ScDrawObjData* pObjData = GetObjData(pObject); 2802 if (pObjData && rRange.Contains(pObjData->maStart)) // Object is in given range 2803 return true; 2804 } 2805 pObject = aIter.Next(); 2806 } 2807 return false; 2808 } 2809 2810 std::vector<SdrObject*> ScDrawLayer::GetObjectsAnchoredToCols(SCTAB nTab, SCCOL nStartCol, 2811 SCCOL nEndCol) 2812 { 2813 SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); 2814 if (!pPage || pPage->GetObjCount() < 1) 2815 return std::vector<SdrObject*>(); 2816 2817 std::vector<SdrObject*> aObjects; 2818 SdrObjListIter aIter(pPage, SdrIterMode::Flat); 2819 SdrObject* pObject = aIter.Next(); 2820 ScRange aRange(nStartCol, 0, nTab, nEndCol, pDoc->MaxRow(), nTab); 2821 while (pObject) 2822 { 2823 ScDrawObjData* pObjData = GetObjData(pObject); 2824 if (pObjData && aRange.Contains(pObjData->maStart)) 2825 aObjects.push_back(pObject); 2826 pObject = aIter.Next(); 2827 } 2828 return aObjects; 2829 } 2830 2831 void ScDrawLayer::MoveObject(SdrObject* pObject, const ScAddress& rNewPosition) 2832 { 2833 // Get anchor data 2834 ScDrawObjData* pObjData = GetObjData(pObject, false); 2835 if (!pObjData) 2836 return; 2837 const ScAddress aOldStart = pObjData->maStart; 2838 const ScAddress aOldEnd = pObjData->maEnd; 2839 2840 // Set start address 2841 pObjData->maStart = rNewPosition; 2842 2843 // Set end address 2844 const SCCOL nObjectColSpan = aOldEnd.Col() - aOldStart.Col(); 2845 const SCROW nObjectRowSpan = aOldEnd.Row() - aOldStart.Row(); 2846 ScAddress aNewEnd = rNewPosition; 2847 aNewEnd.IncRow(nObjectRowSpan); 2848 aNewEnd.IncCol(nObjectColSpan); 2849 pObjData->maEnd = aNewEnd; 2850 2851 // Update draw object according to new anchor 2852 RecalcPos(pObject, *pObjData, false, false); 2853 } 2854 2855 ScDrawObjData* ScDrawLayer::GetNonRotatedObjData( SdrObject* pObj, bool bCreate ) 2856 { 2857 sal_uInt16 nCount = pObj ? pObj->GetUserDataCount() : 0; 2858 sal_uInt16 nFound = 0; 2859 for( sal_uInt16 i = 0; i < nCount; i++ ) 2860 { 2861 SdrObjUserData* pData = pObj->GetUserData( i ); 2862 if( pData && pData->GetInventor() == SdrInventor::ScOrSwDraw && pData->GetId() == SC_UD_OBJDATA && ++nFound == 2 ) 2863 return static_cast<ScDrawObjData*>(pData); 2864 } 2865 if( pObj && bCreate ) 2866 { 2867 ScDrawObjData* pData = new ScDrawObjData; 2868 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2869 return pData; 2870 } 2871 return nullptr; 2872 } 2873 2874 ScDrawObjData* ScDrawLayer::GetObjData( SdrObject* pObj, bool bCreate ) 2875 { 2876 if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_OBJDATA)) 2877 return static_cast<ScDrawObjData*>(pData); 2878 2879 if( pObj && bCreate ) 2880 { 2881 ScDrawObjData* pData = new ScDrawObjData; 2882 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2883 return pData; 2884 } 2885 return nullptr; 2886 } 2887 2888 ScDrawObjData* ScDrawLayer::GetObjDataTab( SdrObject* pObj, SCTAB nTab ) 2889 { 2890 ScDrawObjData* pData = GetObjData( pObj ); 2891 if ( pData ) 2892 { 2893 if ( pData->maStart.IsValid() ) 2894 pData->maStart.SetTab( nTab ); 2895 if ( pData->maEnd.IsValid() ) 2896 pData->maEnd.SetTab( nTab ); 2897 } 2898 return pData; 2899 } 2900 2901 bool ScDrawLayer::IsNoteCaption( SdrObject* pObj ) 2902 { 2903 ScDrawObjData* pData = pObj ? GetObjData( pObj ) : nullptr; 2904 return pData && pData->meType == ScDrawObjData::CellNote; 2905 } 2906 2907 ScDrawObjData* ScDrawLayer::GetNoteCaptionData( SdrObject* pObj, SCTAB nTab ) 2908 { 2909 ScDrawObjData* pData = pObj ? GetObjDataTab( pObj, nTab ) : nullptr; 2910 return (pData && pData->meType == ScDrawObjData::CellNote) ? pData : nullptr; 2911 } 2912 2913 ScMacroInfo* ScDrawLayer::GetMacroInfo( SdrObject* pObj, bool bCreate ) 2914 { 2915 if (SdrObjUserData *pData = GetFirstUserDataOfType(pObj, SC_UD_MACRODATA)) 2916 return static_cast<ScMacroInfo*>(pData); 2917 2918 if ( bCreate ) 2919 { 2920 ScMacroInfo* pData = new ScMacroInfo; 2921 pObj->AppendUserData(std::unique_ptr<SdrObjUserData>(pData)); 2922 return pData; 2923 } 2924 return nullptr; 2925 } 2926 2927 void ScDrawLayer::SetGlobalDrawPersist(SfxObjectShell* pPersist) 2928 { 2929 OSL_ENSURE(!pGlobalDrawPersist,"Multiple SetGlobalDrawPersist"); 2930 pGlobalDrawPersist = pPersist; 2931 } 2932 2933 void ScDrawLayer::SetChanged( bool bFlg /* = true */ ) 2934 { 2935 if ( bFlg && pDoc ) 2936 pDoc->SetChartListenerCollectionNeedsUpdate( true ); 2937 FmFormModel::SetChanged( bFlg ); 2938 } 2939 2940 css::uno::Reference< css::uno::XInterface > ScDrawLayer::createUnoModel() 2941 { 2942 css::uno::Reference< css::uno::XInterface > xRet; 2943 if( pDoc && pDoc->GetDocumentShell() ) 2944 xRet = pDoc->GetDocumentShell()->GetModel(); 2945 2946 return xRet; 2947 } 2948 2949 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2950
