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