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