xref: /core/sc/source/core/data/drwlayer.cxx (revision dfb0d118)
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