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