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