1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
26 #include <scitems.hxx>
27 #include <editeng/eeitem.hxx>
28 #include <editeng/fontitem.hxx>
29 #include <editeng/frmdiritem.hxx>
30 #include <sot/exchange.hxx>
31 #include <svx/objfac3d.hxx>
32 #include <svx/xtable.hxx>
33 #include <svx/svdoutl.hxx>
34 #include <svx/svditer.hxx>
35 #include <svx/svdlayer.hxx>
36 #include <svx/svdoashp.hxx>
37 #include <svx/svdobj.hxx>
38 #include <svx/svdocapt.hxx>
39 #include <svx/svdomeas.hxx>
40 #include <svx/svdoole2.hxx>
41 #include <svx/svdopath.hxx>
42 #include <svx/svdundo.hxx>
43 #include <svx/sdsxyitm.hxx>
44 #include <svx/svxids.hrc>
45 #include <svx/sxcecitm.hxx>
46 #include <svx/sdshitm.hxx>
47 #include <svx/sdtditm.hxx>
48 #include <svx/sdtagitm.hxx>
49 #include <svx/xflclit.hxx>
50 #include <svx/xfillit0.hxx>
51 #include <svx/xlineit0.hxx>
52 #include <svx/xlnstit.hxx>
53 #include <svx/xlnstwit.hxx>
54 #include <svx/xlnstcit.hxx>
55 #include <i18nlangtag/mslangid.hxx>
56 #include <editeng/unolingu.hxx>
57 #include <svx/drawitem.hxx>
58 #include <editeng/fhgtitem.hxx>
59 #include <editeng/scriptspaceitem.hxx>
60 #include <sfx2/objsh.hxx>
61 #include <svl/itempool.hxx>
63 #include <vcl/canvastools.hxx>
64 #include <vcl/svapp.hxx>
65 #include <vcl/settings.hxx>
66 #include <tools/globname.hxx>
67 #include <tools/UnitConversion.hxx>
68 #include <osl/diagnose.h>
70 #include <basegfx/polygon/b2dpolygon.hxx>
71 #include <basegfx/polygon/b2dpolygontools.hxx>
73 #include <drwlayer.hxx>
74 #include <drawpage.hxx>
76 #include <document.hxx>
78 #include <userdat.hxx>
79 #include <markdata.hxx>
80 #include <globstr.hrc>
81 #include <scresid.hxx>
85 #include <charthelper.hxx>
87 #include <stlpool.hxx>
88 #include <detfunc.hxx>
89 #include <basegfx/matrix/b2dhommatrix.hxx>
90 #include <clipcontext.hxx>
91 #include <clipparam.hxx>
97 namespace com::sun::star::embed
{ class XEmbeddedObject
; }
99 #define DET_ARROW_OFFSET 1000
101 using namespace ::com::sun::star
;
103 static E3dObjFactory
* pF3d
= nullptr;
104 static sal_uInt16 nInst
= 0;
106 SfxObjectShell
* ScDrawLayer::pGlobalDrawPersist
= nullptr;
108 bool bDrawIsInUndo
= false; //TODO: Member
110 ScUndoObjData::ScUndoObjData( SdrObject
* pObjP
, const ScAddress
& rOS
, const ScAddress
& rOE
,
111 const ScAddress
& rNS
, const ScAddress
& rNE
) :
112 SdrUndoObj( *pObjP
),
120 ScUndoObjData::~ScUndoObjData()
124 void ScUndoObjData::Undo()
126 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( mxObj
.get() );
127 OSL_ENSURE(pData
,"ScUndoObjData: Data missing");
130 pData
->maStart
= aOldStt
;
131 pData
->maEnd
= aOldEnd
;
134 // Undo also an untransformed anchor
135 pData
= ScDrawLayer::GetNonRotatedObjData( mxObj
.get() );
138 pData
->maStart
= aOldStt
;
139 pData
->maEnd
= aOldEnd
;
143 void ScUndoObjData::Redo()
145 ScDrawObjData
* pData
= ScDrawLayer::GetObjData( mxObj
.get() );
146 OSL_ENSURE(pData
,"ScUndoObjData: Data missing");
149 pData
->maStart
= aNewStt
;
150 pData
->maEnd
= aNewEnd
;
153 // Redo also an untransformed anchor
154 pData
= ScDrawLayer::GetNonRotatedObjData( mxObj
.get() );
157 pData
->maStart
= aNewStt
;
158 pData
->maEnd
= aNewEnd
;
162 ScUndoAnchorData::ScUndoAnchorData( SdrObject
* pObjP
, ScDocument
* pDoc
, SCTAB nTab
) :
163 SdrUndoObj( *pObjP
),
167 mbWasCellAnchored
= ScDrawLayer::IsCellAnchored( *pObjP
);
168 mbWasResizeWithCell
= ScDrawLayer::IsResizeWithCell( *pObjP
);
171 ScUndoAnchorData::~ScUndoAnchorData()
175 void ScUndoAnchorData::Undo()
177 // Trigger Object Change
178 if (mxObj
->IsInserted() && mxObj
->getSdrPageFromSdrObject())
180 SdrHint
aHint(SdrHintKind::ObjectChange
, *mxObj
);
181 mxObj
->getSdrModelFromSdrObject().Broadcast(aHint
);
184 if (mbWasCellAnchored
)
185 ScDrawLayer::SetCellAnchoredFromPosition(*mxObj
, *mpDoc
, mnTab
, mbWasResizeWithCell
);
187 ScDrawLayer::SetPageAnchored( *mxObj
);
190 void ScUndoAnchorData::Redo()
192 if (mbWasCellAnchored
)
193 ScDrawLayer::SetPageAnchored( *mxObj
);
195 ScDrawLayer::SetCellAnchoredFromPosition(*mxObj
, *mpDoc
, mnTab
, mbWasResizeWithCell
);
197 // Trigger Object Change
198 if (mxObj
->IsInserted() && mxObj
->getSdrPageFromSdrObject())
200 SdrHint
aHint(SdrHintKind::ObjectChange
, *mxObj
);
201 mxObj
->getSdrModelFromSdrObject().Broadcast(aHint
);
205 ScTabDeletedHint::ScTabDeletedHint( SCTAB nTabNo
) :
206 SfxHint(SfxHintId::ScTabDeleted
),
211 ScTabDeletedHint::~ScTabDeletedHint()
215 ScTabSizeChangedHint::ScTabSizeChangedHint( SCTAB nTabNo
) :
216 SfxHint(SfxHintId::ScTabSizeChanged
),
221 ScTabSizeChangedHint::~ScTabSizeChangedHint()
225 #define MAXMM 10000000
228 static void lcl_ReverseTwipsToMM( tools::Rectangle
& rRect
)
230 rRect
= o3tl::convert(rRect
, o3tl::Length::mm100
, o3tl::Length::twip
);
233 static ScRange
lcl_getClipRangeFromClipDoc(ScDocument
* pClipDoc
, SCTAB nClipTab
)
242 pClipDoc
->GetClipStart(nClipStartX
, nClipStartY
);
243 pClipDoc
->GetClipArea(nClipEndX
, nClipEndY
, true);
244 nClipEndX
+= nClipStartX
;
245 nClipEndY
+= nClipStartY
; // GetClipArea returns the difference
247 return ScRange(nClipStartX
, nClipStartY
, nClipTab
, nClipEndX
, nClipEndY
, nClipTab
);
250 ScDrawLayer::ScDrawLayer( ScDocument
* pDocument
, OUString _aName
) :
253 pGlobalDrawPersist
? pGlobalDrawPersist
: (pDocument
? pDocument
->GetDocumentShell() : nullptr)),
254 aName(std::move( _aName
)),
257 bAdjustEnabled( true ),
258 bHyphenatorSet( false )
260 SetVOCInvalidationIsReliable(true);
261 m_bThemedControls
= false;
263 pGlobalDrawPersist
= nullptr; // Only use once
265 ScDocShell
* pObjSh
= pDocument
? pDocument
->GetDocumentShell() : nullptr;
266 XColorListRef pXCol
= XColorList::GetStdColorList();
269 SetObjectShell( pObjSh
);
272 const SvxColorListItem
* pColItem
= pObjSh
->GetItem( SID_COLOR_TABLE
);
274 pXCol
= pColItem
->GetColorList();
276 SetPropertyList( static_cast<XPropertyList
*> (pXCol
.get()) );
280 SetScaleUnit(MapUnit::Map100thMM
);
281 SfxItemPool
& rPool
= GetItemPool();
282 rPool
.SetDefaultMetric(MapUnit::Map100thMM
);
283 SvxFrameDirectionItem
aModeItem( SvxFrameDirection::Environment
, EE_PARA_WRITINGDIR
);
284 rPool
.SetUserDefaultItem( aModeItem
);
287 // Set shadow distance defaults as PoolDefaultItems. Details see bug.
288 rPool
.SetUserDefaultItem(makeSdrShadowXDistItem(300));
289 rPool
.SetUserDefaultItem(makeSdrShadowYDistItem(300));
291 // default for script spacing depends on locale, see SdDrawDocument ctor in sd
292 LanguageType eOfficeLanguage
= Application::GetSettings().GetLanguageTag().getLanguageType();
293 if (MsLangId::isKorean(eOfficeLanguage
) || eOfficeLanguage
== LANGUAGE_JAPANESE
)
295 // secondary is edit engine pool
296 rPool
.GetSecondaryPool()->SetUserDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING
) );
299 SetStyleSheetPool(pDocument
? pDocument
->GetStyleSheetPool() : new ScStyleSheetPool(rPool
, pDocument
));
301 SdrLayerAdmin
& rAdmin
= GetLayerAdmin();
302 rAdmin
.NewLayer(u
"vorne"_ustr
, SC_LAYER_FRONT
.get());
303 rAdmin
.NewLayer(u
"hinten"_ustr
, SC_LAYER_BACK
.get());
304 rAdmin
.NewLayer(u
"intern"_ustr
, SC_LAYER_INTERN
.get());
305 // tdf#140252 use same name as in ctor of SdrLayerAdmin
306 rAdmin
.NewLayer(rAdmin
.GetControlLayerName(), SC_LAYER_CONTROLS
.get());
307 rAdmin
.NewLayer(u
"hidden"_ustr
, SC_LAYER_HIDDEN
.get());
309 // Set link for URL-Fields
310 ScModule
* pScMod
= ScModule::get();
311 Outliner
& rOutliner
= GetDrawOutliner();
312 rOutliner
.SetCalcFieldValueHdl( LINK( pScMod
, ScModule
, CalcFieldValueHdl
) );
313 rOutliner
.SetStyleSheetPool(static_cast<SfxStyleSheetPool
*>(GetStyleSheetPool()));
315 Outliner
& rHitOutliner
= GetHitTestOutliner();
316 rHitOutliner
.SetCalcFieldValueHdl( LINK( pScMod
, ScModule
, CalcFieldValueHdl
) );
317 rHitOutliner
.SetStyleSheetPool(static_cast<SfxStyleSheetPool
*>(GetStyleSheetPool()));
319 // set FontHeight pool defaults without changing static SdrEngineDefaults
320 SfxItemPool
* pOutlinerPool
= rOutliner
.GetEditTextObjectPool();
323 m_pItemPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT
)); // 12Pt
324 m_pItemPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK
)); // 12Pt
325 m_pItemPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL
)); // 12Pt
327 SfxItemPool
* pHitOutlinerPool
= rHitOutliner
.GetEditTextObjectPool();
328 if ( pHitOutlinerPool
)
330 pHitOutlinerPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT
)); // 12Pt
331 pHitOutlinerPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CJK
)); // 12Pt
332 pHitOutlinerPool
->SetUserDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT_CTL
)); // 12Pt
335 // initial undo mode as in Calc document
337 EnableUndo( pDoc
->IsUndoEnabled() );
339 // URL-Buttons have no handler anymore, all is done by themselves
343 pF3d
= new E3dObjFactory
;
347 ScDrawLayer::~ScDrawLayer()
349 Broadcast(SdrHint(SdrHintKind::ModelCleared
));
361 void ScDrawLayer::CreateDefaultStyles()
364 auto pSheet
= &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_STANDARD
), SfxStyleFamily::Frame
, SfxStyleSearchBits::ScStandard
);
365 SetDefaultStyleSheet(static_cast<SfxStyleSheet
*>(pSheet
));
368 pSheet
= &GetStyleSheetPool()->Make(ScResId(STR_STYLENAME_NOTE
), SfxStyleFamily::Frame
, SfxStyleSearchBits::ScStandard
);
370 // caption tail arrow
371 ::basegfx::B2DPolygon aTriangle
;
372 aTriangle
.append(::basegfx::B2DPoint(10.0, 0.0));
373 aTriangle
.append(::basegfx::B2DPoint(0.0, 30.0));
374 aTriangle
.append(::basegfx::B2DPoint(20.0, 30.0));
375 aTriangle
.setClosed(true);
377 auto pSet
= &pSheet
->GetItemSet();
378 pSet
->Put(XLineStartItem(OUString(), ::basegfx::B2DPolyPolygon(aTriangle
)).checkForUniqueItem(*this));
379 pSet
->Put(XLineStartWidthItem(200));
380 pSet
->Put(XLineStartCenterItem(false));
381 pSet
->Put(XLineStyleItem(drawing::LineStyle_SOLID
));
382 pSet
->Put(XFillStyleItem(drawing::FillStyle_SOLID
));
383 pSet
->Put(XFillColorItem(OUString(), ScDetectiveFunc::GetCommentColor()));
384 pSet
->Put(SdrCaptionEscDirItem(SdrCaptionEscDir::BestFit
));
387 pSet
->Put(makeSdrShadowItem(true));
388 pSet
->Put(makeSdrShadowXDistItem(100));
389 pSet
->Put(makeSdrShadowYDistItem(100));
392 pSet
->Put(makeSdrTextLeftDistItem(100));
393 pSet
->Put(makeSdrTextRightDistItem(100));
394 pSet
->Put(makeSdrTextUpperDistItem(100));
395 pSet
->Put(makeSdrTextLowerDistItem(100));
396 pSet
->Put(makeSdrTextAutoGrowWidthItem(false));
397 pSet
->Put(makeSdrTextAutoGrowHeightItem(true));
400 SfxItemSet
aEditSet(GetItemPool());
401 ScPatternAttr::FillToEditItemSet(aEditSet
, pDoc
->getCellAttributeHelper().getDefaultCellAttribute().GetItemSet());
403 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTINFO
));
404 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTINFO_CJK
));
405 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTINFO_CTL
));
407 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTHEIGHT
));
408 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTHEIGHT_CJK
));
409 pSet
->Put(aEditSet
.Get(EE_CHAR_FONTHEIGHT_CTL
));
412 void ScDrawLayer::UseHyphenator()
416 css::uno::Reference
< css::linguistic2::XHyphenator
>
417 xHyphenator
= LinguMgr::GetHyphenator();
419 GetDrawOutliner().SetHyphenator( xHyphenator
);
420 GetHitTestOutliner().SetHyphenator( xHyphenator
);
422 bHyphenatorSet
= true;
426 rtl::Reference
<SdrPage
> ScDrawLayer::AllocPage(bool bMasterPage
)
428 return new ScDrawPage(*this, bMasterPage
);
431 bool ScDrawLayer::HasObjects() const
435 sal_uInt16 nCount
= GetPageCount();
436 for (sal_uInt16 i
=0; i
<nCount
&& !bFound
; i
++)
437 if (GetPage(i
)->GetObjCount())
443 SdrModel
* ScDrawLayer::AllocModel() const
445 // Allocated model (for clipboard etc) must not have a pointer
446 // to the original model's document, pass NULL as document:
447 auto pNewModel
= std::make_unique
<ScDrawLayer
>(nullptr, aName
);
448 auto pNewPool
= static_cast<ScStyleSheetPool
*>(pNewModel
->GetStyleSheetPool());
449 pNewPool
->CopyUsedGraphicStylesFrom(GetStyleSheetPool());
451 return pNewModel
.release();
454 bool ScDrawLayer::ScAddPage( SCTAB nTab
)
457 return false; // not inserted
459 rtl::Reference
<ScDrawPage
> pPage
= static_cast<ScDrawPage
*>(AllocPage( false ).get());
460 InsertPage(pPage
.get(), static_cast<sal_uInt16
>(nTab
));
462 AddCalcUndo(std::make_unique
<SdrUndoNewPage
>(*pPage
));
464 ResetTab(nTab
, pDoc
->GetTableCount()-1);
465 return true; // inserted
468 void ScDrawLayer::ScRemovePage( SCTAB nTab
)
473 Broadcast( ScTabDeletedHint( nTab
) );
476 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
477 AddCalcUndo(std::make_unique
<SdrUndoDelPage
>(*pPage
)); // Undo-Action becomes the page owner
478 RemovePage( static_cast<sal_uInt16
>(nTab
) ); // just deliver, not deleting
481 DeletePage( static_cast<sal_uInt16
>(nTab
) ); // just get rid of it
483 ResetTab(nTab
, pDoc
->GetTableCount()-1);
486 void ScDrawLayer::ScRenamePage( SCTAB nTab
, const OUString
& rNewName
)
488 ScDrawPage
* pPage
= static_cast<ScDrawPage
*>( GetPage(static_cast<sal_uInt16
>(nTab
)) );
490 pPage
->SetName(rNewName
);
493 void ScDrawLayer::ScMovePage( sal_uInt16 nOldPos
, sal_uInt16 nNewPos
)
495 MovePage( nOldPos
, nNewPos
);
496 sal_uInt16 nMinPos
= std::min(nOldPos
, nNewPos
);
497 ResetTab(nMinPos
, pDoc
->GetTableCount()-1);
500 void ScDrawLayer::ScCopyPage( sal_uInt16 nOldPos
, sal_uInt16 nNewPos
)
505 SdrPage
* pOldPage
= GetPage(nOldPos
);
506 SdrPage
* pNewPage
= GetPage(nNewPos
);
510 if (pOldPage
&& pNewPage
)
512 SCTAB nOldTab
= static_cast<SCTAB
>(nOldPos
);
513 SCTAB nNewTab
= static_cast<SCTAB
>(nNewPos
);
515 SdrObjListIter
aIter( pOldPage
, SdrIterMode::Flat
);
516 while (SdrObject
* pOldObject
= aIter
.Next())
518 ScDrawObjData
* pOldData
= GetObjData(pOldObject
);
521 pOldData
->maStart
.SetTab(nOldTab
);
522 pOldData
->maEnd
.SetTab(nOldTab
);
525 // Clone to target SdrModel
526 rtl::Reference
<SdrObject
> pNewObject(pOldObject
->CloneSdrObject(*this));
527 pNewObject
->NbcMove(Size(0,0));
528 pNewPage
->InsertObject( pNewObject
.get() );
529 ScDrawObjData
* pNewData
= GetObjData(pNewObject
.get());
532 pNewData
->maStart
.SetTab(nNewTab
);
533 pNewData
->maEnd
.SetTab(nNewTab
);
537 AddCalcUndo( std::make_unique
<SdrUndoInsertObj
>( *pNewObject
) );
541 ResetTab(static_cast<SCTAB
>(nNewPos
), pDoc
->GetTableCount()-1);
544 void ScDrawLayer::ResetTab( SCTAB nStart
, SCTAB nEnd
)
546 SCTAB nPageSize
= static_cast<SCTAB
>(GetPageCount());
548 // No drawing pages exist.
551 if (nEnd
>= nPageSize
)
552 // Avoid iterating beyond the last existing page.
553 nEnd
= nPageSize
- 1;
555 for (SCTAB i
= nStart
; i
<= nEnd
; ++i
)
557 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(i
));
561 SdrObjListIter
aIter(pPage
, SdrIterMode::Flat
);
562 while (SdrObject
* pObj
= aIter
.Next())
564 ScDrawObjData
* pData
= GetObjData(pObj
);
568 pData
->maStart
.SetTab(i
);
569 pData
->maEnd
.SetTab(i
);
574 static bool IsInBlock( const ScAddress
& rPos
, SCCOL nCol1
,SCROW nRow1
, SCCOL nCol2
,SCROW nRow2
)
576 return rPos
.Col() >= nCol1
&& rPos
.Col() <= nCol2
&&
577 rPos
.Row() >= nRow1
&& rPos
.Row() <= nRow2
;
580 void ScDrawLayer::MoveCells( SCTAB nTab
, SCCOL nCol1
,SCROW nRow1
, SCCOL nCol2
,SCROW nRow2
,
581 SCCOL nDx
,SCROW nDy
, bool bUpdateNoteCaptionPos
)
583 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
584 OSL_ENSURE(pPage
,"Page not found");
588 bool bNegativePage
= pDoc
&& pDoc
->IsNegativePage( nTab
);
590 for (const rtl::Reference
<SdrObject
>& pObj
: *pPage
)
592 ScDrawObjData
* pData
= GetObjDataTab( pObj
.get(), nTab
);
595 const ScAddress aOldStt
= pData
->maStart
;
596 const ScAddress aOldEnd
= pData
->maEnd
;
597 bool bChange
= false;
598 if ( aOldStt
.IsValid() && IsInBlock( aOldStt
, nCol1
,nRow1
, nCol2
,nRow2
) )
600 pData
->maStart
.IncCol( nDx
);
601 pData
->maStart
.IncRow( nDy
);
604 if ( aOldEnd
.IsValid() && IsInBlock( aOldEnd
, nCol1
,nRow1
, nCol2
,nRow2
) )
606 pData
->maEnd
.IncCol( nDx
);
607 pData
->maEnd
.IncRow( nDy
);
612 if ( dynamic_cast<const SdrRectObj
*>( pObj
.get()) != nullptr && pData
->maStart
.IsValid() && pData
->maEnd
.IsValid() )
613 pData
->maStart
.PutInOrder( pData
->maEnd
);
615 // Update also an untransformed anchor that's what we stored ( and still do ) to xml
616 ScDrawObjData
* pNoRotatedAnchor
= GetNonRotatedObjData( pObj
.get() );
617 if ( pNoRotatedAnchor
)
619 const ScAddress aOldSttNoRotatedAnchor
= pNoRotatedAnchor
->maStart
;
620 const ScAddress aOldEndNoRotatedAnchor
= pNoRotatedAnchor
->maEnd
;
621 if ( aOldSttNoRotatedAnchor
.IsValid() && IsInBlock( aOldSttNoRotatedAnchor
, nCol1
,nRow1
, nCol2
,nRow2
) )
623 pNoRotatedAnchor
->maStart
.IncCol(nDx
);
624 pNoRotatedAnchor
->maStart
.IncRow(nDy
);
626 if ( aOldEndNoRotatedAnchor
.IsValid() && IsInBlock( aOldEndNoRotatedAnchor
, nCol1
,nRow1
, nCol2
,nRow2
) )
628 pNoRotatedAnchor
->maEnd
.IncCol(nDx
);
629 pNoRotatedAnchor
->maEnd
.IncRow(nDy
);
633 AddCalcUndo( std::make_unique
<ScUndoObjData
>( pObj
.get(), aOldStt
, aOldEnd
, pData
->maStart
, pData
->maEnd
) );
634 RecalcPos( pObj
.get(), *pData
, bNegativePage
, bUpdateNoteCaptionPos
);
640 void ScDrawLayer::SetPageSize(sal_uInt16 nPageNo
, const Size
& rSize
, bool bUpdateNoteCaptionPos
,
641 const ScObjectHandling eObjectHandling
)
643 SdrPage
* pPage
= GetPage(nPageNo
);
647 if ( rSize
!= pPage
->GetSize() )
649 pPage
->SetSize( rSize
);
650 Broadcast( ScTabSizeChangedHint( static_cast<SCTAB
>(nPageNo
) ) ); // SetWorkArea() on the views
653 // Do not call RecalcPos while loading, because row height is not finished, when SetPageSize
654 // is called first time. Instead the objects are initialized from ScXMLImport::endDocument() and
655 // RecalcPos is called from there.
656 if (!pDoc
|| pDoc
->IsImportingXML())
659 // Implement Detective lines (adjust to new heights / widths)
660 // even if size is still the same
661 // (individual rows/columns can have been changed))
663 bool bNegativePage
= pDoc
->IsNegativePage( static_cast<SCTAB
>(nPageNo
) );
665 // Disable mass broadcasts from drawing objects' position changes.
666 bool bWasLocked
= isLocked();
669 for (const rtl::Reference
<SdrObject
>& pObj
: *pPage
)
671 ScDrawObjData
* pData
= GetObjDataTab( pObj
.get(), static_cast<SCTAB
>(nPageNo
) );
672 if( pData
) // cell anchored
674 if (pData
->meType
== ScDrawObjData::DrawingObject
675 || pData
->meType
== ScDrawObjData::ValidationCircle
)
677 switch (eObjectHandling
)
679 case ScObjectHandling::RecalcPosMode
:
680 RecalcPos(pObj
.get(), *pData
, bNegativePage
, bUpdateNoteCaptionPos
);
682 case ScObjectHandling::MoveRTLMode
:
685 case ScObjectHandling::MirrorRTLMode
:
686 MirrorRTL(pObj
.get());
690 else // DetectiveArrow and CellNote
691 RecalcPos(pObj
.get(), *pData
, bNegativePage
, bUpdateNoteCaptionPos
);
693 else // page anchored
695 switch (eObjectHandling
)
697 case ScObjectHandling::MoveRTLMode
:
700 case ScObjectHandling::MirrorRTLMode
:
701 MirrorRTL(pObj
.get());
703 case ScObjectHandling::RecalcPosMode
: // does not occur for page anchored shapes
714 //Can't have a zero width dimension
715 tools::Rectangle
lcl_makeSafeRectangle(const tools::Rectangle
&rNew
)
717 tools::Rectangle aRect
= rNew
;
718 if (aRect
.Bottom() == aRect
.Top())
719 aRect
.SetBottom( aRect
.Top()+1 );
720 if (aRect
.Right() == aRect
.Left())
721 aRect
.SetRight( aRect
.Left()+1 );
725 Point
lcl_calcAvailableDiff(const ScDocument
&rDoc
, SCCOL nCol
, SCROW nRow
, SCTAB nTab
, const Point
&aWantedDiff
)
727 Point
aAvailableDiff(aWantedDiff
);
728 tools::Long nHeight
= o3tl::convert(rDoc
.GetRowHeight( nRow
, nTab
), o3tl::Length::twip
, o3tl::Length::mm100
);
729 tools::Long nWidth
= o3tl::convert(rDoc
.GetColWidth( nCol
, nTab
), o3tl::Length::twip
, o3tl::Length::mm100
);
730 if (aAvailableDiff
.Y() > nHeight
)
731 aAvailableDiff
.setY( nHeight
);
732 if (aAvailableDiff
.X() > nWidth
)
733 aAvailableDiff
.setX( nWidth
);
734 return aAvailableDiff
;
737 tools::Rectangle
lcl_UpdateCalcPoly(basegfx::B2DPolygon
&rCalcPoly
, int nWhichPoint
, const Point
&rPos
)
739 rCalcPoly
.setB2DPoint(nWhichPoint
, basegfx::B2DPoint(rPos
.X(), rPos
.Y()));
740 basegfx::B2DRange
aRange(basegfx::utils::getRange(rCalcPoly
));
741 return tools::Rectangle(static_cast<tools::Long
>(aRange
.getMinX()), static_cast<tools::Long
>(aRange
.getMinY()),
742 static_cast<tools::Long
>(aRange
.getMaxX()), static_cast<tools::Long
>(aRange
.getMaxY()));
745 bool lcl_AreRectanglesApproxEqual(const tools::Rectangle
& rRectA
, const tools::Rectangle
& rRectB
)
747 // Twips <-> Hmm conversions introduce +-1 differences although there are no real changes in the object.
748 // Therefore test with == is not appropriate in some cases.
749 if (std::abs(rRectA
.Left() - rRectB
.Left()) > 1)
751 if (std::abs(rRectA
.Top() - rRectB
.Top()) > 1)
753 if (std::abs(rRectA
.Right() - rRectB
.Right()) > 1)
755 if (std::abs(rRectA
.Bottom() - rRectB
.Bottom()) > 1)
760 bool lcl_NeedsMirrorYCorrection(const SdrObject
* pObj
)
762 return pObj
->GetObjIdentifier() == SdrObjKind::CustomShape
763 && static_cast<const SdrObjCustomShape
*>(pObj
)->IsMirroredY();
766 void lcl_SetLogicRectFromAnchor(SdrObject
* pObj
, const ScDrawObjData
& rAnchor
, const ScDocument
* pDoc
)
768 // This is only used during initialization. At that time, shape handling is always LTR. No need
769 // to consider negative page.
770 if (!pObj
|| !pDoc
|| !rAnchor
.maEnd
.IsValid() || !rAnchor
.maStart
.IsValid())
773 // In case of a vertical mirrored custom shape, LibreOffice uses internally an additional 180deg
774 // in aGeo.nRotationAngle and in turn has a different logic rectangle position. We remove flip,
775 // set the logic rectangle, and apply flip again. You cannot simple use a 180deg-rotated
776 // rectangle, because custom shape mirroring is internally applied after the other
778 const bool bNeedsMirrorYCorrection
= lcl_NeedsMirrorYCorrection(pObj
); // remember state
779 if (bNeedsMirrorYCorrection
)
781 const tools::Rectangle
aRect(pObj
->GetSnapRect());
782 const Point
aLeft(aRect
.Left(), (aRect
.Top() + aRect
.Bottom()) >> 1);
783 const Point
aRight(aLeft
.X() + 1000, aLeft
.Y());
784 pObj
->NbcMirror(aLeft
, aRight
);
787 // Build full sized logic rectangle from start and end given in anchor.
788 const tools::Rectangle
aStartCellRect(
789 pDoc
->GetMMRect(rAnchor
.maStart
.Col(), rAnchor
.maStart
.Row(), rAnchor
.maStart
.Col(),
790 rAnchor
.maStart
.Row(), rAnchor
.maStart
.Tab(), false /*bHiddenAsZero*/));
791 Point
aStartPoint(aStartCellRect
.Left(), aStartCellRect
.Top());
792 aStartPoint
.AdjustX(rAnchor
.maStartOffset
.getX());
793 aStartPoint
.AdjustY(rAnchor
.maStartOffset
.getY());
795 const tools::Rectangle
aEndCellRect(
796 pDoc
->GetMMRect(rAnchor
.maEnd
.Col(), rAnchor
.maEnd
.Row(), rAnchor
.maEnd
.Col(),
797 rAnchor
.maEnd
.Row(), rAnchor
.maEnd
.Tab(), false /*bHiddenAsZero*/));
799 Point
aEndPoint(aEndCellRect
.Left(), aEndCellRect
.Top());
800 aEndPoint
.AdjustX(rAnchor
.maEndOffset
.getX());
801 aEndPoint
.AdjustY(rAnchor
.maEndOffset
.getY());
803 // Set this as new, full sized logical rectangle
804 tools::Rectangle
aNewRectangle(aStartPoint
, aEndPoint
);
805 aNewRectangle
.Normalize();
806 if (!lcl_AreRectanglesApproxEqual(pObj
->GetLogicRect(), aNewRectangle
))
807 pObj
->NbcSetLogicRect(lcl_makeSafeRectangle(aNewRectangle
));
809 // The shape has the correct logical rectangle now. Reapply the above removed mirroring.
810 if (bNeedsMirrorYCorrection
)
812 const tools::Rectangle
aRect(pObj
->GetSnapRect());
813 const Point
aLeft(aRect
.Left(), (aRect
.Top() + aRect
.Bottom()) >> 1);
814 const Point
aRight(aLeft
.X() + 1000, aLeft
.Y());
815 pObj
->NbcMirror(aLeft
, aRight
);
821 void ScDrawLayer::ResizeLastRectFromAnchor(const SdrObject
* pObj
, ScDrawObjData
& rData
,
822 bool bNegativePage
, bool bCanResize
)
824 tools::Rectangle aRect
= pObj
->GetSnapRect();
825 SCCOL nCol1
= rData
.maStart
.Col();
826 SCROW nRow1
= rData
.maStart
.Row();
827 SCTAB nTab1
= rData
.maStart
.Tab();
828 SCCOL nCol2
= rData
.maEnd
.Col();
829 SCROW nRow2
= rData
.maEnd
.Row();
830 SCTAB nTab2
= rData
.maEnd
.Tab();
831 Point
aPos(pDoc
->GetColOffset(nCol1
, nTab1
, /*bHiddenAsZero*/true),
832 pDoc
->GetRowOffset(nRow1
, nTab1
, /*bHiddenAsZero*/true));
833 aPos
.setX(convertTwipToMm100(aPos
.X()));
834 aPos
.setY(convertTwipToMm100(aPos
.Y()));
835 aPos
+= lcl_calcAvailableDiff(*pDoc
, nCol1
, nRow1
, nTab1
, rData
.maStartOffset
);
837 // this sets the needed changed position (translation)
842 // all this stuff is additional stuff to evtl. not only translate the
843 // range (Rectangle), but also check for and evtl. do corrections for it's size
844 const tools::Rectangle
aLastCellRect(rData
.getLastCellRect());
846 // If the row was hidden before, or we don't have a valid cell rect, calculate the
847 // new rect based on the end point.
848 // Also when the end point is set, we need to consider it.
849 if (rData
.mbWasInHiddenRow
|| aLastCellRect
.IsEmpty() || nRow1
!= nRow2
|| nCol1
!= nCol2
)
851 Point
aEnd(pDoc
->GetColOffset(nCol2
, nTab2
, /*bHiddenAsZero*/true),
852 pDoc
->GetRowOffset(nRow2
, nTab2
, /*bHiddenAsZero*/true));
853 aEnd
.setX(convertTwipToMm100(aEnd
.X()));
854 aEnd
.setY(convertTwipToMm100(aEnd
.Y()));
855 aEnd
+= lcl_calcAvailableDiff(*pDoc
, nCol2
, nRow2
, nTab2
, rData
.maEndOffset
);
857 aRect
= tools::Rectangle(aPos
, aEnd
);
859 else if (!aLastCellRect
.IsEmpty())
861 // We calculate based on the last cell rect to be able to scale the image
862 // as much as the cell was scaled.
863 // Still, we keep the image in its current cell (to keep start anchor == end anchor)
864 const tools::Rectangle
aCurrentCellRect(GetCellRect(*GetDocument(), rData
.maStart
, true));
865 tools::Long
nCurrentWidth(aCurrentCellRect
.GetWidth());
866 tools::Long
nCurrentHeight(aCurrentCellRect
.GetHeight());
867 const tools::Long
nLastWidth(aLastCellRect
.GetWidth());
868 const tools::Long
nLastHeight(aLastCellRect
.GetHeight());
870 // tdf#116931 Avoid and correct nifty numerical problems with the integer
871 // based and converted values (GetCellRect uses multiplies with HMM_PER_TWIPS)
872 if(nCurrentWidth
+ 1 == nLastWidth
|| nCurrentWidth
== nLastWidth
+ 1)
874 nCurrentWidth
= nLastWidth
;
877 if(nCurrentHeight
+ 1 == nLastHeight
|| nCurrentHeight
== nLastHeight
+ 1)
879 nCurrentHeight
= nLastHeight
;
882 // get initial ScalingFactors
883 double fWidthFactor(nCurrentWidth
== nLastWidth
|| 0 == nLastWidth
885 : static_cast<double>(nCurrentWidth
) / static_cast<double>(nLastWidth
));
886 double fHeightFactor(nCurrentHeight
== nLastHeight
|| 0 == nLastHeight
888 : static_cast<double>(nCurrentHeight
) / static_cast<double>(nLastHeight
));
890 // check if we grow or shrink - and at all
891 const bool bIsGrowing(nCurrentWidth
> nLastWidth
|| nCurrentHeight
> nLastHeight
);
892 const bool bIsShrinking(nCurrentWidth
< nLastWidth
|| nCurrentHeight
< nLastHeight
);
893 const bool bIsSizeChanged(bIsGrowing
|| bIsShrinking
);
895 // handle AspectRatio, only needed if size does change
896 if(bIsSizeChanged
&& pObj
->shouldKeepAspectRatio())
898 tools::Rectangle aRectIncludingOffset
= aRect
;
899 aRectIncludingOffset
.setWidth(aRect
.GetWidth() + rData
.maStartOffset
.X());
900 aRectIncludingOffset
.setHeight(aRect
.GetHeight() + rData
.maStartOffset
.Y());
901 tools::Long nWidth
= aRectIncludingOffset
.GetWidth();
902 assert(nWidth
&& "div-by-zero");
903 double fMaxWidthFactor
= static_cast<double>(nCurrentWidth
)
904 / static_cast<double>(nWidth
);
905 tools::Long nHeight
= aRectIncludingOffset
.GetHeight();
906 assert(nHeight
&& "div-by-zero");
907 double fMaxHeightFactor
= static_cast<double>(nCurrentHeight
)
908 / static_cast<double>(nHeight
);
909 double fMaxFactor
= std::min(fMaxHeightFactor
, fMaxWidthFactor
);
911 if(bIsGrowing
) // cell is growing larger
913 // To actually grow the image, we need to take the max
914 fWidthFactor
= std::max(fWidthFactor
, fHeightFactor
);
916 else if(bIsShrinking
) // cell is growing smaller, take the min
918 fWidthFactor
= std::min(fWidthFactor
, fHeightFactor
);
921 // We don't want the image to become larger than the current cell
922 fWidthFactor
= fHeightFactor
= std::min(fWidthFactor
, fMaxFactor
);
927 // tdf#116931 re-organized scaling (if needed)
928 // Check if we need to scale at all. Always scale on growing.
929 bool bNeedToScale(bIsGrowing
);
931 if(!bNeedToScale
&& bIsShrinking
)
933 // Check if original still fits into space. Do *not* forget to
934 // compare with evtl. numerically corrected aCurrentCellRect
935 const bool bFitsInX(aRect
.Right() <= aCurrentCellRect
.Left() + nCurrentWidth
);
936 const bool bFitsInY(aRect
.Bottom() <= aCurrentCellRect
.Top() + nCurrentHeight
);
938 // If the image still fits in the smaller cell, don't resize it at all
939 bNeedToScale
= (!bFitsInX
|| !bFitsInY
);
944 // tdf#116931 use transformations now. Translation is already applied
945 // (see aRect.SetPos above), so only scale needs to be applied - relative
946 // to *new* CellRect (which is aCurrentCellRect).
947 // Prepare scale relative to top-left of aCurrentCellRect
948 basegfx::B2DHomMatrix aChange
;
950 aChange
.translate(-aCurrentCellRect
.Left(), -aCurrentCellRect
.Top());
951 aChange
.scale(fWidthFactor
, fHeightFactor
);
952 aChange
.translate(aCurrentCellRect
.Left(), aCurrentCellRect
.Top());
954 // create B2DRange and transform by prepared scale
955 basegfx::B2DRange aNewRange
= vcl::unotools::b2DRectangleFromRectangle(aRect
);
957 aNewRange
.transform(aChange
);
960 aRect
= tools::Rectangle(
961 basegfx::fround
<tools::Long
>(aNewRange
.getMinX()), basegfx::fround
<tools::Long
>(aNewRange
.getMinY()),
962 basegfx::fround
<tools::Long
>(aNewRange
.getMaxX()), basegfx::fround
<tools::Long
>(aNewRange
.getMaxY()));
969 MirrorRectRTL(aRect
);
971 rData
.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect
), pObj
->IsVisible());
974 void ScDrawLayer::InitializeCellAnchoredObj(SdrObject
* pObj
, ScDrawObjData
& rData
)
976 // This is called from ScXMLImport::endDocument()
979 if (!rData
.getShapeRect().IsEmpty())
980 return; // already initialized, should not happen
981 if (rData
.meType
== ScDrawObjData::CellNote
|| rData
.meType
== ScDrawObjData::ValidationCircle
982 || rData
.meType
== ScDrawObjData::DetectiveArrow
)
983 return; // handled in RecalcPos
985 // Prevent multiple broadcasts during the series of changes.
986 bool bWasLocked
= pObj
->getSdrModelFromSdrObject().isLocked();
987 pObj
->getSdrModelFromSdrObject().setLock(true);
989 // rNoRotatedAnchor refers in its start and end addresses and its start and end offsets to
990 // the logic rectangle of the object. The values are so, as if no hidden columns and rows
991 // exists and if it is a LTR sheet. These values are directly used for XML in ODF file.
992 ScDrawObjData
& rNoRotatedAnchor
= *GetNonRotatedObjData(pObj
, true /*bCreate*/);
994 // From XML import, rData contains temporarily the anchor information as they are given in
995 // XML. Copy it to rNoRotatedAnchor, where it belongs. rData will later contain the anchor
996 // of the transformed object as visible on screen.
997 rNoRotatedAnchor
.maStart
= rData
.maStart
;
998 rNoRotatedAnchor
.maEnd
= rData
.maEnd
;
999 rNoRotatedAnchor
.maStartOffset
= rData
.maStartOffset
;
1000 rNoRotatedAnchor
.maEndOffset
= rData
.maEndOffset
;
1002 SCCOL nCol1
= rNoRotatedAnchor
.maStart
.Col();
1003 SCROW nRow1
= rNoRotatedAnchor
.maStart
.Row();
1004 SCTAB nTab1
= rNoRotatedAnchor
.maStart
.Tab(); // Used as parameter several times
1006 // Object has coordinates relative to left/top of containing cell in XML. Change object to
1007 // absolute coordinates as internally used.
1008 const tools::Rectangle
aRect(
1009 pDoc
->GetMMRect(nCol1
, nRow1
, nCol1
, nRow1
, nTab1
, false /*bHiddenAsZero*/));
1010 const Size
aShift(aRect
.Left(), aRect
.Top());
1011 pObj
->NbcMove(aShift
);
1013 const ScAnchorType aAnchorType
= ScDrawLayer::GetAnchorType(*pObj
);
1014 if (aAnchorType
== SCA_CELL_RESIZE
)
1016 if (pObj
->GetObjIdentifier() == SdrObjKind::Line
)
1018 // Horizontal lines might have wrong start and end anchor because of erroneously applied
1019 // 180deg rotation (tdf#137446). Other lines have wrong end anchor. Coordinates in
1020 // object are correct. Use them for recreating the anchor.
1021 const basegfx::B2DPolygon
aPoly(
1022 static_cast<SdrPathObj
*>(pObj
)->GetPathPoly().getB2DPolygon(0));
1023 const basegfx::B2DPoint
aB2DPoint0(aPoly
.getB2DPoint(0));
1024 const basegfx::B2DPoint
aB2DPoint1(aPoly
.getB2DPoint(1));
1025 const Point
aPointLT(basegfx::fround
<tools::Long
>(std::min(aB2DPoint0
.getX(), aB2DPoint1
.getX())),
1026 basegfx::fround
<tools::Long
>(std::min(aB2DPoint0
.getY(), aB2DPoint1
.getY())));
1027 const Point
aPointRB(basegfx::fround
<tools::Long
>(std::max(aB2DPoint0
.getX(), aB2DPoint1
.getX())),
1028 basegfx::fround
<tools::Long
>(std::max(aB2DPoint0
.getY(), aB2DPoint1
.getY())));
1029 const tools::Rectangle
aObjRect(aPointLT
, aPointRB
);
1030 GetCellAnchorFromPosition(aObjRect
, rNoRotatedAnchor
, *pDoc
, nTab1
,
1031 false /*bHiddenAsZero*/);
1033 else if (pObj
->GetObjIdentifier() == SdrObjKind::Measure
)
1035 // Measure lines might have got wrong start and end anchor from XML import. Recreate
1036 // anchor from start and end point.
1037 SdrMeasureObj
* pMeasureObj
= static_cast<SdrMeasureObj
*>(pObj
);
1038 // tdf#137576. The logic rectangle has likely no current values here, but only the
1039 // 1cm x 1cm default size. The call of TakeUnrotatedSnapRect is currently (LO 7.2)
1040 // the only way to force a recalc of the logic rectangle.
1041 tools::Rectangle aObjRect
;
1042 pMeasureObj
->TakeUnrotatedSnapRect(aObjRect
);
1043 GetCellAnchorFromPosition(aObjRect
, rNoRotatedAnchor
, *pDoc
, rData
.maStart
.Tab(),
1044 false /*bHiddenAsZero*/);
1046 else if (pObj
->IsResizeProtect())
1048 // tdf#154005: This is a workaround for documents created with LO 6 and older.
1049 rNoRotatedAnchor
.mbResizeWithCell
= false;
1050 rData
.mbResizeWithCell
= false;
1051 UpdateCellAnchorFromPositionEnd(*pObj
, rNoRotatedAnchor
, *pDoc
, nTab1
,
1052 true /*bUseLogicRect*/);
1054 else if (pObj
->GetObjIdentifier() == SdrObjKind::Group
)
1060 // In case there are hidden rows or cols, versions 7.0 and earlier have written width and
1061 // height in file so that hidden row or col are count as zero. XML import bases the
1062 // logical rectangle of the object on it. Shapes have at least wrong size, when row or col
1063 // are shown. We try to regenerate the logic rectangle as far as possible from the anchor.
1064 // ODF specifies anyway, that the size has to be ignored, if end cell attributes exist.
1065 lcl_SetLogicRectFromAnchor(pObj
, rNoRotatedAnchor
, pDoc
);
1068 else // aAnchorType == SCA_CELL
1070 // XML has no end cell address in this case. We generate it from position.
1071 UpdateCellAnchorFromPositionEnd(*pObj
, rNoRotatedAnchor
, *pDoc
, nTab1
,
1072 true /*bUseLogicRect*/);
1075 // Make sure maShapeRect of rNoRotatedAnchor is not empty. Method ScDrawView::Notify()
1076 // needs it to detect a change in object geometry. For example a 180deg rotation effects only
1078 rNoRotatedAnchor
.setShapeRect(GetDocument(), pObj
->GetLogicRect(), true);
1080 // Start and end addresses and offsets in rData refer to the actual snap rectangle of the
1081 // shape. We initialize them here based on the "full" sized object. Adaptation to reduced size
1082 // (by hidden row/col) is done later in RecalcPos.
1083 GetCellAnchorFromPosition(pObj
->GetSnapRect(), rData
, *pDoc
, nTab1
, false /*bHiddenAsZero*/);
1085 // As of ODF 1.3 strict there is no attribute to store whether an object is hidden. So a "visible"
1086 // object might actually be hidden by being in hidden row or column. We detect it here.
1087 // Note, that visibility by hidden row or column refers to the snap rectangle.
1088 if (pObj
->IsVisible()
1089 && (pDoc
->RowHidden(rData
.maStart
.Row(), rData
.maStart
.Tab())
1090 || pDoc
->ColHidden(rData
.maStart
.Col(), rData
.maStart
.Tab())))
1091 pObj
->SetVisible(false);
1093 // Set visibility. ToDo: Really used?
1094 rNoRotatedAnchor
.setShapeRect(GetDocument(), pObj
->GetLogicRect(), pObj
->IsVisible());
1096 // And set maShapeRect in rData. It stores not only the current rectangles, but currently,
1097 // existence of maShapeRect is the flag for initialization is done.
1098 rData
.setShapeRect(GetDocument(), pObj
->GetSnapRect(), pObj
->IsVisible());
1100 pObj
->getSdrModelFromSdrObject().setLock(bWasLocked
);
1103 void ScDrawLayer::RecalcPos( SdrObject
* pObj
, ScDrawObjData
& rData
, bool bNegativePage
, bool bUpdateNoteCaptionPos
)
1105 OSL_ENSURE( pDoc
, "ScDrawLayer::RecalcPos - missing document" );
1109 if (rData
.meType
== ScDrawObjData::CellNote
)
1111 OSL_ENSURE( rData
.maStart
.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" );
1112 /* #i109372# On insert/remove rows/columns/cells: Updating the caption
1113 position must not be done, if the cell containing the note has not
1114 been moved yet in the document. The calling code now passes an
1115 additional boolean stating if the cells are already moved. */
1116 /* tdf #152081 Do not change hidden objects. That would produce zero height
1117 or width and loss of caption.*/
1118 if (bUpdateNoteCaptionPos
&& pObj
->IsVisible())
1120 /* When inside an undo action, there may be pending note captions
1121 where cell note is already deleted (thus document cannot find
1122 the note object anymore). The caption will be deleted later
1123 with drawing undo. */
1124 if( ScPostIt
* pNote
= pDoc
->GetNote( rData
.maStart
) )
1125 pNote
->UpdateCaptionPos( rData
.maStart
);
1130 bool bValid1
= rData
.maStart
.IsValid();
1131 SCCOL nCol1
= rData
.maStart
.Col();
1132 SCROW nRow1
= rData
.maStart
.Row();
1133 SCTAB nTab1
= rData
.maStart
.Tab();
1134 bool bValid2
= rData
.maEnd
.IsValid();
1135 SCCOL nCol2
= rData
.maEnd
.Col();
1136 SCROW nRow2
= rData
.maEnd
.Row();
1137 SCTAB nTab2
= rData
.maEnd
.Tab();
1139 if (rData
.meType
== ScDrawObjData::ValidationCircle
)
1141 // Validation circle for detective.
1142 rData
.setShapeRect(GetDocument(), pObj
->GetLogicRect());
1144 // rData.maStart should contain the address of the be validated cell.
1145 tools::Rectangle aRect
= GetCellRect(*GetDocument(), rData
.maStart
, true);
1146 aRect
.AdjustLeft( -250 );
1147 aRect
.AdjustRight(250 );
1148 aRect
.AdjustTop( -70 );
1149 aRect
.AdjustBottom(70 );
1150 if ( bNegativePage
)
1151 MirrorRectRTL( aRect
);
1153 if ( pObj
->GetLogicRect() != aRect
)
1156 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1157 rData
.setShapeRect(GetDocument(), lcl_makeSafeRectangle(aRect
));
1158 // maStart has the meaning of "to be validated cell" in a validation circle. For usual
1159 // drawing objects it has the meaning "left/top of logic/snap rect". Because the rectangle
1160 // is expanded above, SetLogicRect() will set maStart to one cell left and one cell above
1161 // of the to be validated cell. We need to backup the old value and restore it.
1162 ScAddress
aBackup(rData
.maStart
);
1163 pObj
->SetLogicRect(rData
.getShapeRect());
1164 rData
.maStart
= aBackup
;
1167 else if (rData
.meType
== ScDrawObjData::DetectiveArrow
)
1169 rData
.setShapeRect(GetDocument(), pObj
->GetLogicRect());
1170 basegfx::B2DPolygon aCalcPoly
;
1171 Point
aOrigStartPos(pObj
->GetPoint(0));
1172 Point
aOrigEndPos(pObj
->GetPoint(1));
1173 aCalcPoly
.append(basegfx::B2DPoint(aOrigStartPos
.X(), aOrigStartPos
.Y()));
1174 aCalcPoly
.append(basegfx::B2DPoint(aOrigEndPos
.X(), aOrigEndPos
.Y()));
1175 //TODO: do not create multiple Undos for one object (last one can be omitted then)
1181 Point
aPos( pDoc
->GetColOffset( nCol1
, nTab1
), pDoc
->GetRowOffset( nRow1
, nTab1
) );
1182 if (!pDoc
->ColHidden(nCol1
, nTab1
, nullptr, &nLastCol
))
1183 aPos
.AdjustX(pDoc
->GetColWidth( nCol1
, nTab1
) / 4 );
1184 if (!pDoc
->RowHidden(nRow1
, nTab1
, nullptr, &nLastRow
))
1185 aPos
.AdjustY(pDoc
->GetRowHeight( nRow1
, nTab1
) / 2 );
1186 aPos
.setX(convertTwipToMm100(aPos
.X()));
1187 aPos
.setY(convertTwipToMm100(aPos
.Y()));
1188 Point aStartPos
= aPos
;
1189 if ( bNegativePage
)
1190 aStartPos
.setX( -aStartPos
.X() ); // don't modify aPos - used below
1191 if ( pObj
->GetPoint( 0 ) != aStartPos
)
1194 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1196 rData
.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly
, 0, aStartPos
));
1197 pObj
->SetPoint( aStartPos
, 0 );
1202 Point
aEndPos( aPos
.X() + DET_ARROW_OFFSET
, aPos
.Y() - DET_ARROW_OFFSET
);
1203 if (aEndPos
.Y() < 0)
1204 aEndPos
.AdjustY(2 * DET_ARROW_OFFSET
);
1205 if ( bNegativePage
)
1206 aEndPos
.setX( -aEndPos
.X() );
1207 if ( pObj
->GetPoint( 1 ) != aEndPos
)
1210 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1212 rData
.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly
, 1, aEndPos
));
1213 pObj
->SetPoint( aEndPos
, 1 );
1219 Point
aPos( pDoc
->GetColOffset( nCol2
, nTab2
), pDoc
->GetRowOffset( nRow2
, nTab2
) );
1220 if (!pDoc
->ColHidden(nCol2
, nTab2
, nullptr, &nLastCol
))
1221 aPos
.AdjustX(pDoc
->GetColWidth( nCol2
, nTab2
) / 4 );
1222 if (!pDoc
->RowHidden(nRow2
, nTab2
, nullptr, &nLastRow
))
1223 aPos
.AdjustY(pDoc
->GetRowHeight( nRow2
, nTab2
) / 2 );
1224 aPos
.setX(convertTwipToMm100(aPos
.X()));
1225 aPos
.setY(convertTwipToMm100(aPos
.Y()));
1226 Point aEndPos
= aPos
;
1227 if ( bNegativePage
)
1228 aEndPos
.setX( -aEndPos
.X() ); // don't modify aPos - used below
1229 if ( pObj
->GetPoint( 1 ) != aEndPos
)
1232 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1234 rData
.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly
, 1, aEndPos
));
1235 pObj
->SetPoint( aEndPos
, 1 );
1240 Point
aStartPos( aPos
.X() - DET_ARROW_OFFSET
, aPos
.Y() - DET_ARROW_OFFSET
);
1241 if (aStartPos
.X() < 0)
1242 aStartPos
.AdjustX(2 * DET_ARROW_OFFSET
);
1243 if (aStartPos
.Y() < 0)
1244 aStartPos
.AdjustY(2 * DET_ARROW_OFFSET
);
1245 if ( bNegativePage
)
1246 aStartPos
.setX( -aStartPos
.X() );
1247 if ( pObj
->GetPoint( 0 ) != aStartPos
)
1250 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1252 rData
.setShapeRect(GetDocument(), lcl_UpdateCalcPoly(aCalcPoly
, 0, aStartPos
));
1253 pObj
->SetPoint( aStartPos
, 0 );
1257 } // end ScDrawObjData::DetectiveArrow
1258 else // start ScDrawObjData::DrawingObject
1260 // Do not change hidden objects. That would produce zero height or width and loss of offsets.
1261 if (!pObj
->IsVisible())
1264 // Prevent multiple broadcasts during the series of changes.
1265 bool bWasLocked
= pObj
->getSdrModelFromSdrObject().isLocked();
1266 pObj
->getSdrModelFromSdrObject().setLock(true);
1268 bool bCanResize
= bValid2
&& !pObj
->IsResizeProtect() && rData
.mbResizeWithCell
;
1270 // update anchor with snap rect
1271 ResizeLastRectFromAnchor( pObj
, rData
, bNegativePage
, bCanResize
);
1273 ScDrawObjData
& rNoRotatedAnchor
= *GetNonRotatedObjData( pObj
, true /*bCreate*/);
1277 tools::Rectangle aNew
= rData
.getShapeRect();
1278 tools::Rectangle
aOld(pObj
->GetSnapRect());
1279 if (!lcl_AreRectanglesApproxEqual(aNew
, aOld
))
1282 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1284 // ToDo: Implement NbcSetSnapRect of SdrMeasureObj. Then this can be removed.
1285 tools::Long nOldWidth
= aOld
.GetWidth();
1286 tools::Long nOldHeight
= aOld
.GetHeight();
1287 if (pObj
->IsPolyObj() && nOldWidth
&& nOldHeight
)
1289 // Polyline objects need special treatment.
1290 Size
aSizeMove(aNew
.Left()-aOld
.Left(), aNew
.Top()-aOld
.Top());
1291 pObj
->NbcMove(aSizeMove
);
1293 double fXFrac
= static_cast<double>(aNew
.GetWidth()) / static_cast<double>(nOldWidth
);
1294 double fYFrac
= static_cast<double>(aNew
.GetHeight()) / static_cast<double>(nOldHeight
);
1295 pObj
->NbcResize(aNew
.TopLeft(), Fraction(fXFrac
), Fraction(fYFrac
));
1298 rData
.setShapeRect(GetDocument(), lcl_makeSafeRectangle(rData
.getShapeRect()), pObj
->IsVisible());
1299 if (pObj
->GetObjIdentifier() == SdrObjKind::CustomShape
)
1300 pObj
->AdjustToMaxRect(rData
.getShapeRect());
1302 pObj
->SetSnapRect(rData
.getShapeRect());
1304 // The shape rectangle in the 'unrotated' anchor needs to be updated to the changed
1305 // object geometry. It is used in adjustAnchoredPosition() in ScDrawView::Notify().
1306 rNoRotatedAnchor
.setShapeRect(pDoc
, pObj
->GetLogicRect(), pObj
->IsVisible());
1311 const Point
aPos(rData
.getShapeRect().TopLeft());
1312 if ( pObj
->GetRelativePos() != aPos
)
1315 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
1316 pObj
->SetRelativePos( aPos
);
1317 rNoRotatedAnchor
.setShapeRect(pDoc
, pObj
->GetLogicRect(), pObj
->IsVisible());
1321 * If we were not allowed resize the object, then the end cell anchor
1322 * is possibly incorrect now, and if the object has no end-cell (e.g.
1323 * missing in original .xml) we are also forced to generate one
1325 bool bEndAnchorIsBad
= !bValid2
|| pObj
->IsResizeProtect();
1326 if (bEndAnchorIsBad
)
1328 // update 'rotated' anchor
1329 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj
, rData
, *pDoc
, nTab1
, false);
1330 // update 'unrotated' anchor
1331 ScDrawLayer::UpdateCellAnchorFromPositionEnd(*pObj
, rNoRotatedAnchor
, *pDoc
, nTab1
);
1334 // End prevent multiple broadcasts during the series of changes.
1335 pObj
->getSdrModelFromSdrObject().setLock(bWasLocked
);
1337 pObj
->BroadcastObjectChange();
1338 } // end ScDrawObjData::DrawingObject
1341 bool ScDrawLayer::GetPrintArea( ScRange
& rRange
, bool bSetHor
, bool bSetVer
) const
1343 OSL_ENSURE( pDoc
, "ScDrawLayer::GetPrintArea without document" );
1347 SCTAB nTab
= rRange
.aStart
.Tab();
1348 OSL_ENSURE( rRange
.aEnd
.Tab() == nTab
, "GetPrintArea: Tab differ" );
1350 bool bNegativePage
= pDoc
->IsNegativePage( nTab
);
1353 tools::Long nEndX
= 0;
1354 tools::Long nEndY
= 0;
1355 tools::Long nStartX
= LONG_MAX
;
1356 tools::Long nStartY
= LONG_MAX
;
1358 // Calculate borders
1363 SCCOL nStartCol
= rRange
.aStart
.Col();
1365 for (i
=0; i
<nStartCol
; i
++)
1366 nStartX
+=pDoc
->GetColWidth(i
,nTab
);
1368 SCCOL nEndCol
= rRange
.aEnd
.Col();
1369 for (i
=nStartCol
; i
<=nEndCol
; i
++)
1370 nEndX
+= pDoc
->GetColWidth(i
,nTab
);
1371 nStartX
= convertTwipToMm100(nStartX
);
1372 nEndX
= convertTwipToMm100(nEndX
);
1376 nStartY
= pDoc
->GetRowHeight( 0, rRange
.aStart
.Row()-1, nTab
);
1377 nEndY
= nStartY
+ pDoc
->GetRowHeight( rRange
.aStart
.Row(),
1378 rRange
.aEnd
.Row(), nTab
);
1379 nStartY
= convertTwipToMm100(nStartY
);
1380 nEndY
= convertTwipToMm100(nEndY
);
1383 if ( bNegativePage
)
1385 nStartX
= -nStartX
; // positions are negative, swap start/end so the same comparisons work
1387 ::std::swap( nStartX
, nEndX
);
1390 const SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
1391 OSL_ENSURE(pPage
,"Page not found");
1394 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1395 while (SdrObject
* pObject
= aIter
.Next())
1397 //TODO: test Flags (hidden?)
1399 tools::Rectangle aObjRect
= pObject
->GetCurrentBoundRect();
1401 if ( !bSetHor
&& ( aObjRect
.Right() < nStartX
|| aObjRect
.Left() > nEndX
) )
1403 if ( !bSetVer
&& ( aObjRect
.Bottom() < nStartY
|| aObjRect
.Top() > nEndY
) )
1405 // #i104716# don't include hidden note objects
1406 if ( bFit
&& pObject
->GetLayer() != SC_LAYER_HIDDEN
)
1410 if (aObjRect
.Left() < nStartX
) nStartX
= aObjRect
.Left();
1411 if (aObjRect
.Right() > nEndX
) nEndX
= aObjRect
.Right();
1415 if (aObjRect
.Top() < nStartY
) nStartY
= aObjRect
.Top();
1416 if (aObjRect
.Bottom() > nEndY
) nEndY
= aObjRect
.Bottom();
1423 if ( bNegativePage
)
1425 nStartX
= -nStartX
; // reverse transformation, so the same cell address calculation works
1427 ::std::swap( nStartX
, nEndX
);
1432 OSL_ENSURE( nStartX
<=nEndX
&& nStartY
<=nEndY
, "Start/End wrong in ScDrawLayer::GetPrintArea" );
1436 nStartX
= o3tl::toTwips(nStartX
, o3tl::Length::mm100
);
1437 nEndX
= o3tl::toTwips(nEndX
, o3tl::Length::mm100
);
1441 rRange
.aStart
.SetCol( 0 );
1442 if (nWidth
<= nStartX
)
1444 for (SCCOL nCol
: pDoc
->GetColumnsRange(nTab
, 0, pDoc
->MaxCol()))
1446 nWidth
+= pDoc
->GetColWidth(nCol
,nTab
);
1447 if (nWidth
> nStartX
)
1449 rRange
.aStart
.SetCol( nCol
);
1456 rRange
.aEnd
.SetCol( 0 );
1457 if (nWidth
<= nEndX
)
1459 for (SCCOL nCol
: pDoc
->GetColumnsRange(nTab
, 0, pDoc
->MaxCol())) //TODO: start at Start
1461 nWidth
+= pDoc
->GetColWidth(nCol
,nTab
);
1464 rRange
.aEnd
.SetCol( nCol
);
1473 nStartY
= o3tl::toTwips(nStartY
, o3tl::Length::mm100
);
1474 nEndY
= o3tl::toTwips(nEndY
, o3tl::Length::mm100
);
1475 SCROW nRow
= pDoc
->GetRowForHeight( nTab
, nStartY
);
1476 rRange
.aStart
.SetRow( nRow
>0 ? (nRow
-1) : 0);
1477 nRow
= pDoc
->GetRowForHeight( nTab
, nEndY
);
1478 rRange
.aEnd
.SetRow( nRow
== pDoc
->MaxRow() ? pDoc
->MaxRow() :
1479 (nRow
>0 ? (nRow
-1) : 0));
1486 rRange
.aStart
.SetCol(0);
1487 rRange
.aEnd
.SetCol(0);
1491 rRange
.aStart
.SetRow(0);
1492 rRange
.aEnd
.SetRow(0);
1498 void ScDrawLayer::AddCalcUndo( std::unique_ptr
<SdrUndoAction
> pUndo
)
1503 pUndoGroup
.reset(new SdrUndoGroup(*this));
1505 pUndoGroup
->AddAction( std::move(pUndo
) );
1509 void ScDrawLayer::BeginCalcUndo(bool bDisableTextEditUsesCommonUndoManager
)
1511 SetDisableTextEditUsesCommonUndoManager(bDisableTextEditUsesCommonUndoManager
);
1516 std::unique_ptr
<SdrUndoGroup
> ScDrawLayer::GetCalcUndo()
1518 std::unique_ptr
<SdrUndoGroup
> pRet
= std::move(pUndoGroup
);
1520 SetDisableTextEditUsesCommonUndoManager(false);
1524 void ScDrawLayer::MoveArea( SCTAB nTab
, SCCOL nCol1
,SCROW nRow1
, SCCOL nCol2
,SCROW nRow2
,
1525 SCCOL nDx
,SCROW nDy
, bool bInsDel
, bool bUpdateNoteCaptionPos
)
1527 OSL_ENSURE( pDoc
, "ScDrawLayer::MoveArea without document" );
1531 if (!bAdjustEnabled
)
1534 bool bNegativePage
= pDoc
->IsNegativePage( nTab
);
1536 tools::Rectangle aRect
= pDoc
->GetMMRect( nCol1
, nRow1
, nCol2
, nRow2
, nTab
);
1537 lcl_ReverseTwipsToMM( aRect
);
1538 //TODO: use twips directly?
1543 for (SCCOL s
=0; s
<nDx
; s
++)
1544 aMove
.AdjustX(pDoc
->GetColWidth(s
+nCol1
,nTab
) );
1546 for (SCCOL s
=-1; s
>=nDx
; s
--)
1547 aMove
.AdjustX( -(pDoc
->GetColWidth(s
+nCol1
,nTab
)) );
1549 aMove
.AdjustY(pDoc
->GetRowHeight( nRow1
, nRow1
+nDy
-1, nTab
) );
1551 aMove
.AdjustY( -sal_Int16(pDoc
->GetRowHeight( nRow1
+nDy
, nRow1
-1, nTab
)) );
1553 if ( bNegativePage
)
1554 aMove
.setX( -aMove
.X() );
1556 Point aTopLeft
= aRect
.TopLeft(); // Beginning when zoomed out
1559 if ( aMove
.X() != 0 && nDx
< 0 ) // nDx counts cells, sign is independent of RTL
1560 aTopLeft
.AdjustX(aMove
.X() );
1561 if ( aMove
.Y() < 0 )
1562 aTopLeft
.AdjustY(aMove
.Y() );
1565 // Detectiv arrows: Adjust cell position
1567 MoveCells( nTab
, nCol1
,nRow1
, nCol2
,nRow2
, nDx
,nDy
, bUpdateNoteCaptionPos
);
1570 bool ScDrawLayer::HasObjectsInRows( SCTAB nTab
, SCROW nStartRow
, SCROW nEndRow
)
1572 OSL_ENSURE( pDoc
, "ScDrawLayer::HasObjectsInRows without document" );
1576 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
1577 OSL_ENSURE(pPage
,"Page not found");
1581 // for an empty page, there's no need to calculate the row heights
1582 if (!pPage
->GetObjCount())
1585 tools::Rectangle aTestRect
;
1587 aTestRect
.AdjustTop(pDoc
->GetRowHeight( 0, nStartRow
-1, nTab
) );
1589 if (nEndRow
==pDoc
->MaxRow())
1590 aTestRect
.SetBottom( MAXMM
);
1593 aTestRect
.SetBottom( aTestRect
.Top() );
1594 aTestRect
.AdjustBottom(pDoc
->GetRowHeight( nStartRow
, nEndRow
, nTab
) );
1595 aTestRect
.SetBottom(convertTwipToMm100(aTestRect
.Bottom()));
1598 aTestRect
.SetTop(convertTwipToMm100(aTestRect
.Top()));
1600 aTestRect
.SetLeft( 0 );
1601 aTestRect
.SetRight( MAXMM
);
1603 bool bNegativePage
= pDoc
->IsNegativePage( nTab
);
1604 if ( bNegativePage
)
1605 MirrorRectRTL( aTestRect
);
1607 tools::Rectangle aObjRect
;
1608 SdrObjListIter
aIter( pPage
);
1609 while (SdrObject
* pObject
= aIter
.Next())
1611 aObjRect
= pObject
->GetSnapRect(); //TODO: GetLogicRect ?
1612 if (aTestRect
.Contains(aObjRect
.TopLeft()) || aTestRect
.Contains(aObjRect
.BottomLeft()))
1619 void ScDrawLayer::DeleteObjectsInArea( SCTAB nTab
, SCCOL nCol1
,SCROW nRow1
,
1620 SCCOL nCol2
,SCROW nRow2
, bool bAnchored
)
1622 OSL_ENSURE( pDoc
, "ScDrawLayer::DeleteObjectsInArea without document" );
1626 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
1627 OSL_ENSURE(pPage
,"Page ?");
1631 pPage
->RecalcObjOrdNums();
1633 const size_t nObjCount
= pPage
->GetObjCount();
1637 tools::Rectangle aDelRect
= pDoc
->GetMMRect( nCol1
, nRow1
, nCol2
, nRow2
, nTab
);
1638 tools::Rectangle aDelCircle
= aDelRect
;
1639 aDelCircle
.AdjustLeft(-250);
1640 aDelCircle
.AdjustRight(250);
1641 aDelCircle
.AdjustTop(-70);
1642 aDelCircle
.AdjustBottom(70);
1644 std::vector
<SdrObject
*> ppObj
;
1645 ppObj
.reserve(nObjCount
);
1647 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1648 while (SdrObject
* pObject
= aIter
.Next())
1650 // do not delete note caption, they are always handled by the cell note
1651 // TODO: detective objects are still deleted, is this desired?
1652 if (!IsNoteCaption( pObject
))
1654 tools::Rectangle aObjRect
;
1655 ScDrawObjData
* pObjData
= ScDrawLayer::GetObjData(pObject
);
1656 if (pObjData
&& pObjData
->meType
== ScDrawObjData::ValidationCircle
)
1658 aObjRect
= pObject
->GetLogicRect();
1659 if(aDelCircle
.Contains(aObjRect
))
1660 ppObj
.push_back(pObject
);
1664 aObjRect
= pObject
->GetCurrentBoundRect();
1665 if (aDelRect
.Contains(aObjRect
))
1669 ScAnchorType aAnchorType
= ScDrawLayer::GetAnchorType(*pObject
);
1670 if (aAnchorType
== SCA_CELL
|| aAnchorType
== SCA_CELL_RESIZE
)
1671 ppObj
.push_back(pObject
);
1674 ppObj
.push_back(pObject
);
1681 for (auto p
: ppObj
)
1682 AddCalcUndo(std::make_unique
<SdrUndoDelObj
>(*p
));
1684 for (auto p
: ppObj
)
1685 pPage
->RemoveObject(p
->GetOrdNum());
1688 void ScDrawLayer::DeleteObjectsInSelection( const ScMarkData
& rMark
)
1690 OSL_ENSURE( pDoc
, "ScDrawLayer::DeleteObjectsInSelection without document" );
1694 if ( !rMark
.IsMultiMarked() )
1697 const ScRange
& aMarkRange
= rMark
.GetMultiMarkArea();
1699 SCTAB nTabCount
= pDoc
->GetTableCount();
1700 for (const SCTAB nTab
: rMark
)
1702 if (nTab
>= nTabCount
)
1705 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
1708 pPage
->RecalcObjOrdNums();
1709 const size_t nObjCount
= pPage
->GetObjCount();
1712 // Rectangle around the whole selection
1713 tools::Rectangle aMarkBound
= pDoc
->GetMMRect(
1714 aMarkRange
.aStart
.Col(), aMarkRange
.aStart
.Row(),
1715 aMarkRange
.aEnd
.Col(), aMarkRange
.aEnd
.Row(), nTab
);
1717 std::vector
<SdrObject
*> ppObj
;
1718 ppObj
.reserve(nObjCount
);
1720 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
1721 while (SdrObject
* pObject
= aIter
.Next())
1723 // do not delete note caption, they are always handled by the cell note
1724 // TODO: detective objects are still deleted, is this desired?
1725 if (!IsNoteCaption( pObject
))
1727 tools::Rectangle aObjRect
= pObject
->GetCurrentBoundRect();
1728 ScRange aRange
= pDoc
->GetRange(nTab
, aObjRect
);
1729 bool bObjectInMarkArea
=
1730 aMarkBound
.Contains(aObjRect
) && rMark
.IsAllMarked(aRange
);
1731 const ScDrawObjData
* pObjData
= ScDrawLayer::GetObjData(pObject
);
1732 ScAnchorType aAnchorType
= ScDrawLayer::GetAnchorType(*pObject
);
1733 bool bObjectAnchoredToMarkedCell
1734 = ((aAnchorType
== SCA_CELL
|| aAnchorType
== SCA_CELL_RESIZE
)
1735 && pObjData
&& pObjData
->maStart
.IsValid()
1736 && rMark
.IsCellMarked(pObjData
->maStart
.Col(),
1737 pObjData
->maStart
.Row()));
1738 if (bObjectInMarkArea
|| bObjectAnchoredToMarkedCell
)
1740 ppObj
.push_back(pObject
);
1745 // Delete objects (backwards)
1748 for (auto p
: ppObj
)
1749 AddCalcUndo(std::make_unique
<SdrUndoDelObj
>(*p
));
1751 for (auto p
: ppObj
)
1752 pPage
->RemoveObject(p
->GetOrdNum());
1762 void ScDrawLayer::CopyToClip( ScDocument
* pClipDoc
, SCTAB nTab
, const tools::Rectangle
& rRange
)
1764 // copy everything in the specified range into the same page (sheet) in the clipboard doc
1766 SdrPage
* pSrcPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
1770 ScDrawLayer
* pDestModel
= nullptr;
1771 SdrPage
* pDestPage
= nullptr;
1773 ScRange aClipRange
= lcl_getClipRangeFromClipDoc(pClipDoc
, nTab
);
1775 SdrObjListIter
aIter( pSrcPage
, SdrIterMode::Flat
);
1776 while (SdrObject
* pOldObject
= aIter
.Next())
1778 // do not copy internal objects (detective) and note captions
1779 if (pOldObject
->GetLayer() == SC_LAYER_INTERN
)
1782 const ScDrawObjData
* pObjData
= ScDrawLayer::GetObjData(pOldObject
);
1783 if (IsNoteCaption(pObjData
))
1786 // Catch objects where the object itself is inside the rectangle to be copied.
1787 bool bObjectInArea
= rRange
.Contains(pOldObject
->GetCurrentBoundRect());
1788 // Catch objects whose anchor is inside the rectangle to be copied.
1789 if (!bObjectInArea
&& pObjData
)
1790 bObjectInArea
= aClipRange
.Contains(pObjData
->maStart
);
1796 pDestModel
= pClipDoc
->GetDrawLayer(); // does the document already have a drawing layer?
1799 // allocate drawing layer in clipboard document only if there are objects to copy
1801 pClipDoc
->InitDrawLayer(); //TODO: create contiguous pages
1802 pDestModel
= pClipDoc
->GetDrawLayer();
1805 pDestPage
= pDestModel
->GetPage(static_cast<sal_uInt16
>(nTab
));
1808 OSL_ENSURE(pDestPage
, "no page");
1811 // Clone to target SdrModel
1812 rtl::Reference
<SdrObject
> pNewObject(pOldObject
->CloneSdrObject(*pDestModel
));
1813 uno::Reference
< chart2::XChartDocument
> xOldChart( ScChartHelper::GetChartFromSdrObject( pOldObject
) );
1814 if(!xOldChart
.is())//#i110034# do not move charts as they lose all their data references otherwise
1818 // The object is anchored to cell. The position is determined by the start
1819 // address. Copying into the clipboard does not change the anchor.
1820 // ToDo: Adapt Offset relative to anchor cell size for cell anchored.
1821 // ToDo: Adapt Offset and size for cell-anchored with resize objects.
1822 // ToDo: Exclude object from resize if disallowed at object.
1826 // The object is anchored to page. We make its position so, that the
1827 // cell behind the object will have the same address in clipboard document as
1828 // in source document. So we will be able to reconstruct the original cell
1829 // address from position when pasting the object.
1830 tools::Rectangle aObjRect
= pOldObject
->GetSnapRect();
1831 ScRange aPseudoAnchor
= pDoc
->GetRange(nTab
, aObjRect
, true /*bHiddenAsZero*/);
1832 tools::Rectangle aSourceCellRect
1833 = GetCellRect(*pDoc
, aPseudoAnchor
.aStart
, false /*bMergedCell*/);
1834 tools::Rectangle aDestCellRect
1835 = GetCellRect(*pClipDoc
, aPseudoAnchor
.aStart
, false);
1836 Point aMove
= aDestCellRect
.TopLeft() - aSourceCellRect
.TopLeft();
1837 pNewObject
->NbcMove(Size(aMove
.getX(), aMove
.getY()));
1841 pDestPage
->InsertObject(pNewObject
.get());
1843 // Store the chart's source data to the clipboard document, even when it's out of the
1844 // copied range. It will be ignored when pasted to the same document; when pasted to
1845 // another document, ScDocument::mpClipParam will provide the actually copied ranges,
1846 // and the data copied here will be used to break connection and switch to own data
1847 // in ScDrawLayer::CopyFromClip.
1848 if (xOldChart
&& !xOldChart
->hasInternalDataProvider())
1850 sc::CopyToClipContext
aCxt(*pClipDoc
, false, true);
1851 OUString aChartName
= static_cast<SdrOle2Obj
*>(pOldObject
)->GetPersistName();
1852 std::vector
<ScRangeList
> aRangesVector
;
1853 pDoc
->GetChartRanges(aChartName
, aRangesVector
, *pDoc
);
1854 for (const ScRangeList
& ranges
: aRangesVector
)
1856 for (const ScRange
& r
: ranges
)
1858 for (SCTAB i
= r
.aStart
.Tab(); i
<= r
.aEnd
.Tab(); ++i
)
1860 ScTable
* pTab
= pDoc
->FetchTable(i
);
1861 ScTable
* pClipTab
= pClipDoc
->FetchTable(i
);
1862 if (!pTab
|| !pClipTab
)
1864 pTab
->CopyToClip(aCxt
, r
.aStart
.Col(), r
.aStart
.Row(), r
.aEnd
.Col(),
1865 r
.aEnd
.Row(), pClipTab
);
1871 // no undo needed in clipboard document
1872 // charts are not updated
1877 static bool lcl_IsAllInRange( const ::std::vector
< ScRangeList
>& rRangesVector
, const ScRange
& rClipRange
)
1879 // check if every range of rRangesVector is completely in rClipRange
1881 for( const ScRangeList
& rRanges
: rRangesVector
)
1883 for ( size_t i
= 0, nCount
= rRanges
.size(); i
< nCount
; i
++ )
1885 const ScRange
& rRange
= rRanges
[ i
];
1886 if ( !rClipRange
.Contains( rRange
) )
1888 return false; // at least one range is not valid
1893 return true; // everything is fine
1896 static bool lcl_MoveRanges( ::std::vector
< ScRangeList
>& rRangesVector
, const ScRange
& rSourceRange
,
1897 const ScAddress
& rDestPos
, const ScDocument
& rDoc
)
1899 bool bChanged
= false;
1901 ScRange
aErrorRange( ScAddress::UNINITIALIZED
);
1902 for( ScRangeList
& rRanges
: rRangesVector
)
1904 for ( size_t i
= 0, nCount
= rRanges
.size(); i
< nCount
; i
++ )
1906 ScRange
& rRange
= rRanges
[ i
];
1907 if ( rSourceRange
.Contains( rRange
) )
1909 SCCOL nDiffX
= rDestPos
.Col() - rSourceRange
.aStart
.Col();
1910 SCROW nDiffY
= rDestPos
.Row() - rSourceRange
.aStart
.Row();
1911 SCTAB nDiffZ
= rDestPos
.Tab() - rSourceRange
.aStart
.Tab();
1912 if (!rRange
.Move( nDiffX
, nDiffY
, nDiffZ
, aErrorRange
, rDoc
))
1914 assert(!"can't move range");
1924 void ScDrawLayer::CopyFromClip(ScDrawLayer
* pClipModel
, SCTAB nSourceTab
,
1925 const ScRange
& rSourceRange
, const ScAddress
& rDestPos
,
1926 const ScRange
& rDestRange
, bool bTransposing
)
1928 OSL_ENSURE( pDoc
, "ScDrawLayer::CopyFromClip without document" );
1935 if (bDrawIsInUndo
) //TODO: can this happen?
1937 OSL_FAIL("CopyFromClip, bDrawIsInUndo");
1941 SCTAB nDestTab
= rDestPos
.Tab();
1943 SdrPage
* pSrcPage
= pClipModel
->GetPage(static_cast<sal_uInt16
>(nSourceTab
));
1944 SdrPage
* pDestPage
= GetPage(static_cast<sal_uInt16
>(nDestTab
));
1945 OSL_ENSURE( pSrcPage
&& pDestPage
, "draw page missing" );
1946 if ( !pSrcPage
|| !pDestPage
)
1949 ScDocument
* pClipDoc
= pClipModel
->GetDocument();
1951 return; // Can this happen? And if yes, what to do?
1953 SdrObjListIter
aIter( pSrcPage
, SdrIterMode::Flat
);
1955 return; // no objects at all. Nothing to do.
1957 // a clipboard document and its source share the same document item pool,
1958 // so the pointers can be compared to see if this is copy&paste within
1959 // the same document
1960 bool bSameDoc
= pDoc
->GetPool() == pClipDoc
->GetPool();
1961 bool bDestClip
= pDoc
->IsClipboard(); // Happens while transposing. ToDo: Other cases?
1963 //#i110034# charts need correct sheet names for xml range conversion during load
1964 //so the target sheet name is temporarily renamed (if we have any SdrObjects)
1965 OUString aDestTabName
;
1966 bool bRestoreDestTabName
= false;
1967 if (!bSameDoc
&& !bDestClip
)
1969 OUString aSourceTabName
;
1970 if (pClipDoc
->GetName(nSourceTab
, aSourceTabName
) && pDoc
->GetName(nDestTab
, aDestTabName
)
1971 && aSourceTabName
!= aDestTabName
&& pDoc
->ValidNewTabName(aSourceTabName
))
1973 bRestoreDestTabName
= pDoc
->RenameTab( nDestTab
, aSourceTabName
);
1977 SCTAB nClipTab
= bRestoreDestTabName
? nDestTab
: nSourceTab
;
1978 ScRange aClipRange
= lcl_getClipRangeFromClipDoc(pClipDoc
, nClipTab
);
1980 // We are going to make all rectangle calculations on LTR, so determine whether doc is RTL.
1981 bool bSourceRTL
= pClipDoc
->IsLayoutRTL(nSourceTab
);
1982 bool bDestRTL
= pDoc
->IsLayoutRTL(nDestTab
);
1984 while (SdrObject
* pOldObject
= aIter
.Next())
1986 // ToDO: Can this happen? Such objects should not be in the clipboard document.
1987 // do not copy internal objects (detective) and note captions
1988 if ((pOldObject
->GetLayer() == SC_LAYER_INTERN
) || IsNoteCaption(pOldObject
))
1991 // 'aIter' considers all objects on pSrcPage. But ScDocument::CopyBlockFromClip, which is used
1992 // for filtered data, acts not on the total range but only on parts of it. So we need to look,
1993 // whether an object is really contained in the current rSourceRange.
1994 // For cell anchored objects we use the start address of the anchor, for page anchored objects
1995 // we use the cell range behind the bounding box of the shape.
1996 ScAddress aSrcObjStart
;
1997 const ScDrawObjData
* pObjData
= ScDrawLayer::GetObjData(pOldObject
);
1998 if (pObjData
) // Object is anchored to cell.
2000 aSrcObjStart
= (*pObjData
).maStart
;
2002 else // Object is anchored to page.
2004 aSrcObjStart
= pClipDoc
->GetRange(nSourceTab
, pOldObject
->GetCurrentBoundRect()).aStart
;
2006 if (!rSourceRange
.Contains(aSrcObjStart
))
2008 // If object is anchored to a filtered cell, we will not copy it, because filtered rows are
2009 // eliminated in paste. Copying would produce hidden objects which can only be accessed per
2011 if (pObjData
&& pClipDoc
->RowFiltered((*pObjData
).maStart
.Row(), nSourceTab
))
2015 auto pStyleSheet
= pOldObject
->GetStyleSheet();
2016 if (pStyleSheet
&& !bSameDoc
)
2017 pDoc
->GetStyleSheetPool()->CopyStyleFrom(pClipModel
->GetStyleSheetPool(),
2018 pStyleSheet
->GetName(),
2019 pStyleSheet
->GetFamily(), true);
2021 rtl::Reference
<SdrObject
> pNewObject(pOldObject
->CloneSdrObject(*this));
2022 tools::Rectangle aObjRect
= pOldObject
->GetSnapRect();
2025 MirrorRTL(pNewObject
.get()); // We make the calculations in LTR.
2026 MirrorRectRTL(aObjRect
);
2029 bool bCanResize
= IsResizeWithCell(*pOldObject
) && !pOldObject
->IsResizeProtect();
2030 // We do not resize charts or other OLE objects and do not resize when transposing.
2031 bCanResize
&= pOldObject
->GetObjIdentifier() != SdrObjKind::OLE2
;
2032 bCanResize
&= !bTransposing
&& !pClipDoc
->GetClipParam().isTransposed();
2035 // Filtered rows are eliminated on paste. Filtered cols do not exist as of May 2023.
2036 // Collapsed or hidden rows/cols are shown on paste.
2037 // Idea: First calculate top left cell and bottom right cell of pasted object. Then
2038 // calculate the object corners inside these cell and from that build new snap rectangle.
2039 // We assume that pObjData is valid and pObjData and aObjRect correspond to each other
2040 // in the source document.
2042 // Start cell of object in source and destination. The case of a filtered start cell is
2043 // already excluded above. aSrcObjStart = (*pObjData).maStart is already done above.
2044 // If filtered rows exist in the total range, the range was divided into blocks which
2045 // do not contain filtered rows. So the rows between start of aSourceRange and object
2046 // start row do not contain filtered rows.
2047 SCROW nStartRowDiff
= aSrcObjStart
.Row() - rSourceRange
.aStart
.Row();
2048 SCCOL nStartColDiff
= aSrcObjStart
.Col() - rSourceRange
.aStart
.Col();
2049 ScAddress aDestObjStart
= rDestRange
.aStart
;
2050 aDestObjStart
.IncCol(nStartColDiff
);
2051 aDestObjStart
.IncRow(nStartRowDiff
);
2053 // End cell of object in source and destination. We look at the amount of rows/cols to be
2054 // added to get object end cell from object start cell.
2055 ScAddress aSrcObjEnd
= (*pObjData
).maEnd
;
2056 SCCOL nColsToAdd
= aSrcObjEnd
.Col() - aSrcObjStart
.Col();
2058 = pClipDoc
->CountNonFilteredRows(aSrcObjStart
.Row(), aSrcObjEnd
.Row(), nSourceTab
)
2060 ScAddress aDestObjEnd
= aDestObjStart
;
2061 aDestObjEnd
.IncCol(nColsToAdd
);
2062 aDestObjEnd
.IncRow(nRowsToAdd
);
2064 // Position of object inside start and end cell in source. We describe the distance from
2065 // cell corner to object corner as ratio of offset to cell width/height.
2066 // We cannot use GetCellRect method, because that uses bHiddenAsZero=true.
2067 Point aSrcObjTopLeftOffset
= (*pObjData
).maStartOffset
;
2068 tools::Rectangle aSrcStartRect
2069 = pClipDoc
->GetMMRect(aSrcObjStart
.Col(), aSrcObjStart
.Row(), aSrcObjStart
.Col(),
2070 aSrcObjStart
.Row(), nSourceTab
, false /*bHiddenAsZero*/);
2072 MirrorRectRTL(aSrcStartRect
);
2074 = aSrcStartRect
.getOpenWidth() == 0
2076 : double(aSrcObjTopLeftOffset
.X()) / double(aSrcStartRect
.getOpenWidth());
2078 = aSrcStartRect
.getOpenHeight() == 0
2080 : double(aSrcObjTopLeftOffset
.Y()) / double(aSrcStartRect
.getOpenHeight());
2082 Point aSrcObjBottomRightOffset
= (*pObjData
).maEndOffset
;
2083 tools::Rectangle aSrcEndRect
2084 = pClipDoc
->GetMMRect(aSrcObjEnd
.Col(), aSrcObjEnd
.Row(), aSrcObjEnd
.Col(),
2085 aSrcObjEnd
.Row(), nSourceTab
, false /*bHiddenAsZero*/);
2087 MirrorRectRTL(aSrcEndRect
);
2089 = aSrcEndRect
.getOpenWidth() == 0
2091 : double(aSrcObjBottomRightOffset
.X()) / double(aSrcEndRect
.getOpenWidth());
2093 = aSrcEndRect
.getOpenHeight() == 0
2095 : double(aSrcObjBottomRightOffset
.Y()) / double(aSrcEndRect
.getOpenHeight());
2096 // The end cell given in pObjData might be filtered. In that case the object is cut at
2097 // the lower cell edge. The offset is as large as the cell.
2098 if (pClipDoc
->RowFiltered(aSrcObjEnd
.Row(), nSourceTab
))
2101 // Position of object inside start and end cell in destination
2102 tools::Rectangle aDestStartRect
2103 = GetCellRect(*pDoc
, aDestObjStart
, false /*bMergedCell*/);
2105 MirrorRectRTL(aDestStartRect
);
2106 Point
aDestObjTopLeftOffset(fStartXRatio
* aDestStartRect
.getOpenWidth(),
2107 fStartYRatio
* aDestStartRect
.getOpenHeight());
2108 Point aDestObjTopLeft
= aDestStartRect
.TopLeft() + aDestObjTopLeftOffset
;
2110 tools::Rectangle aDestEndRect
= GetCellRect(*pDoc
, aDestObjEnd
, false /*bMergedCell*/);
2112 MirrorRectRTL(aDestEndRect
);
2113 Point
aDestObjBottomRightOffset(fEndXRatio
* aDestEndRect
.getOpenWidth(),
2114 fEndYRatio
* aDestEndRect
.getOpenHeight());
2115 Point aDestObjBottomRight
= aDestEndRect
.TopLeft() + aDestObjBottomRightOffset
;
2117 // Fit new object into destination rectangle
2118 tools::Rectangle
aNewObjRect(aDestObjTopLeft
, aDestObjBottomRight
);
2119 aNewObjRect
= lcl_makeSafeRectangle(aNewObjRect
);
2120 if (pNewObject
->GetObjIdentifier() == SdrObjKind::CustomShape
)
2121 pNewObject
->AdjustToMaxRect(aNewObjRect
);
2123 pNewObject
->SetSnapRect(aNewObjRect
);
2127 // We determine the MM-distance of the new object from its start cell in destination from
2128 // the ratio of offset to cell width/height. Thus the object still starts in this cell
2129 // even if the destination cell has different size. Otherwise we might lose objects when
2132 // Start Cell address in source and destination
2133 SCCOLROW nStartRowDiff
= pClipDoc
->CountNonFilteredRows(rSourceRange
.aStart
.Row(),
2134 aSrcObjStart
.Row(), nSourceTab
)
2136 SCCOLROW nStartColDiff
= aSrcObjStart
.Col() - rSourceRange
.aStart
.Col();
2138 std::swap(nStartRowDiff
, nStartColDiff
);
2139 ScAddress aDestObjStart
= rDestRange
.aStart
;
2140 aDestObjStart
.IncCol(nStartColDiff
);
2141 aDestObjStart
.IncRow(nStartRowDiff
);
2143 // Position of object inside start cell in source.
2144 tools::Rectangle aSrcStartRect
2145 = pClipDoc
->GetMMRect(aSrcObjStart
.Col(), aSrcObjStart
.Row(), aSrcObjStart
.Col(),
2146 aSrcObjStart
.Row(), nSourceTab
, false /*bHiddenAsZero*/);
2148 MirrorRectRTL(aSrcStartRect
);
2149 Point aSrcObjTopLeftOffset
= pObjData
? (*pObjData
).maStartOffset
2150 : aObjRect
.TopLeft() - aSrcStartRect
.TopLeft();
2153 = aSrcStartRect
.getOpenWidth() == 0
2155 : double(aSrcObjTopLeftOffset
.X()) / double(aSrcStartRect
.getOpenWidth());
2157 = aSrcStartRect
.getOpenHeight() == 0
2159 : double(aSrcObjTopLeftOffset
.Y()) / double(aSrcStartRect
.getOpenHeight());
2161 // Position of object inside start cell in destination
2162 tools::Rectangle aDestStartRect
2163 = GetCellRect(*pDoc
, aDestObjStart
, false /*bMergedCell*/);
2165 MirrorRectRTL(aDestStartRect
);
2166 Point
aDestObjTopLeftOffset(fStartXRatio
* aDestStartRect
.getOpenWidth(),
2167 fStartYRatio
* aDestStartRect
.getOpenHeight());
2168 Point aDestObjTopLeft
= aDestStartRect
.TopLeft() + aDestObjTopLeftOffset
;
2170 // Move new object to new position
2171 Point aMoveBy
= aDestObjTopLeft
- aObjRect
.TopLeft();
2172 pNewObject
->NbcMove(Size(aMoveBy
.getX(), aMoveBy
.getY()));
2176 MirrorRTL(pNewObject
.get());
2178 // Changing object position or size does not automatically change its anchor.
2179 if (IsCellAnchored(*pOldObject
))
2180 SetCellAnchoredFromPosition(*pNewObject
, *pDoc
, nDestTab
,
2181 IsResizeWithCell(*pOldObject
));
2183 // InsertObject includes broadcasts
2184 // MakeNameUnique makes the pasted objects accessible via Navigator.
2186 pDestPage
->InsertObject(pNewObject
.get());
2190 pDoc
->EnableUndo(false);
2191 pDestPage
->InsertObjectThenMakeNameUnique(pNewObject
.get());
2193 pDoc
->EnableUndo(true);
2197 AddCalcUndo(std::make_unique
<SdrUndoInsertObj
>(*pNewObject
));
2199 //#i110034# handle chart data references (after InsertObject)
2200 if (pNewObject
->GetObjIdentifier() == SdrObjKind::OLE2
)
2202 uno::Reference
<embed::XEmbeddedObject
> xIPObj
2203 = static_cast<SdrOle2Obj
*>(pNewObject
.get())->GetObjRef();
2204 uno::Reference
<embed::XClassifiedObject
> xClassified
= xIPObj
;
2205 SvGlobalName aObjectClassName
;
2206 if (xClassified
.is())
2210 aObjectClassName
= SvGlobalName(xClassified
->getClassID());
2212 catch (uno::Exception
&)
2214 // TODO: handle error?
2218 if (xIPObj
.is() && SotExchange::IsChart(aObjectClassName
))
2220 uno::Reference
<chart2::XChartDocument
> xNewChart(
2221 ScChartHelper::GetChartFromSdrObject(pNewObject
.get()));
2222 if (xNewChart
.is() && !xNewChart
->hasInternalDataProvider())
2225 = static_cast<SdrOle2Obj
*>(pNewObject
.get())->GetPersistName();
2226 ::std::vector
<ScRangeList
> aRangesVector
;
2227 pDoc
->GetChartRanges(aChartName
, aRangesVector
, *pDoc
);
2228 if (!aRangesVector
.empty())
2230 bool bInSourceRange
= false;
2231 bInSourceRange
= lcl_IsAllInRange(aRangesVector
, aClipRange
);
2233 // always lose references when pasting into a clipboard document (transpose)
2234 if ((bInSourceRange
|| bSameDoc
) && !bDestClip
)
2238 if (rDestPos
!= aClipRange
.aStart
)
2240 // update the data ranges to the new (copied) position
2241 if (lcl_MoveRanges(aRangesVector
, aClipRange
, rDestPos
, *pDoc
))
2242 pDoc
->SetChartRanges(aChartName
, aRangesVector
);
2247 // leave the ranges unchanged
2252 // pasting into a new document without the complete source data
2253 // -> break connection to source data and switch to own data
2254 uno::Reference
<chart::XChartDocument
> xOldChartDoc(
2255 ScChartHelper::GetChartFromSdrObject(pOldObject
), uno::UNO_QUERY
);
2256 uno::Reference
<chart::XChartDocument
> xNewChartDoc(xNewChart
,
2258 if (xOldChartDoc
.is() && xNewChartDoc
.is())
2259 xNewChartDoc
->attachData(xOldChartDoc
->getData());
2261 // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc)
2269 if( bRestoreDestTabName
)
2270 pDoc
->RenameTab( nDestTab
, aDestTabName
);
2273 void ScDrawLayer::MirrorRTL( SdrObject
* pObj
)
2275 OSL_ENSURE( pDoc
, "ScDrawLayer::MirrorRTL - missing document" );
2279 SdrObjKind nIdent
= pObj
->GetObjIdentifier();
2281 // don't mirror OLE or graphics, otherwise ask the object
2282 // if it can be mirrored
2283 bool bCanMirror
= ( nIdent
!= SdrObjKind::Graphic
&& nIdent
!= SdrObjKind::OLE2
);
2286 SdrObjTransformInfoRec aInfo
;
2287 pObj
->TakeObjInfo( aInfo
);
2288 bCanMirror
= aInfo
.bMirror90Allowed
;
2293 ScDrawObjData
* pData
= GetObjData(pObj
);
2294 if (pData
) // cell anchored
2296 // Remember values from positive side.
2297 const tools::Rectangle aOldSnapRect
= pObj
->GetSnapRect();
2298 const tools::Rectangle aOldLogicRect
= pObj
->GetLogicRect();
2299 // Generate noRotate anchor if missing.
2300 ScDrawObjData
* pNoRotatedAnchor
= GetNonRotatedObjData(pObj
);
2301 if (!pNoRotatedAnchor
)
2303 ScDrawObjData aNoRotateAnchor
;
2304 const tools::Rectangle
aLogicRect(pObj
->GetLogicRect());
2305 GetCellAnchorFromPosition(aLogicRect
, aNoRotateAnchor
,
2306 *pDoc
, pData
->maStart
.Tab());
2307 aNoRotateAnchor
.mbResizeWithCell
= pData
->mbResizeWithCell
;
2308 SetNonRotatedAnchor(*pObj
, aNoRotateAnchor
);
2309 pNoRotatedAnchor
= GetNonRotatedObjData(pObj
);
2310 assert(pNoRotatedAnchor
);
2312 // Mirror object at vertical axis
2313 Point
aRef1( 0, 0 );
2314 Point
aRef2( 0, 1 );
2316 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
2317 pObj
->Mirror( aRef1
, aRef2
);
2319 // Adapt offsets in pNoRotatedAnchor so, that object will be moved to current position in
2321 const tools::Long nInverseShift
= aOldSnapRect
.Left() + aOldSnapRect
.Right();
2322 const Point aLogicLT
= pObj
->GetLogicRect().TopLeft();
2323 const Point aMirroredLogicLT
= aLogicLT
+ Point(nInverseShift
, 0);
2324 const Point aOffsetDiff
= aMirroredLogicLT
- aOldLogicRect
.TopLeft();
2326 pNoRotatedAnchor
->maStartOffset
+= aOffsetDiff
;
2327 pNoRotatedAnchor
->maEndOffset
+= aOffsetDiff
;
2329 else // page anchored
2331 Point
aRef1( 0, 0 );
2332 Point
aRef2( 0, 1 );
2334 AddCalcUndo( std::make_unique
<SdrUndoGeoObj
>( *pObj
) );
2335 pObj
->Mirror( aRef1
, aRef2
);
2340 // Move instead of mirroring:
2341 // New start position is negative of old end position
2342 // -> move by sum of start and end position
2343 tools::Rectangle aObjRect
= pObj
->GetSnapRect();
2344 Size
aMoveSize( -(aObjRect
.Left() + aObjRect
.Right()), 0 );
2346 AddCalcUndo( std::make_unique
<SdrUndoMoveObj
>( *pObj
, aMoveSize
) );
2347 pObj
->Move( aMoveSize
);
2350 // for cell anchored objects adapt rectangles in anchors
2351 ScDrawObjData
* pData
= GetObjData(pObj
);
2354 pData
->setShapeRect(GetDocument(), pObj
->GetSnapRect(), pObj
->IsVisible());
2355 ScDrawObjData
* pNoRotatedAnchor
= GetNonRotatedObjData(pObj
, true /*bCreate*/);
2356 pNoRotatedAnchor
->setShapeRect(GetDocument(), pObj
->GetLogicRect(), pObj
->IsVisible());
2360 void ScDrawLayer::MoveRTL(SdrObject
* pObj
)
2362 tools::Rectangle aObjRect
= pObj
->GetSnapRect();
2363 Size
aMoveSize( -(aObjRect
.Left() + aObjRect
.Right()), 0 );
2365 AddCalcUndo( std::make_unique
<SdrUndoMoveObj
>( *pObj
, aMoveSize
) );
2366 pObj
->Move( aMoveSize
);
2368 // for cell anchored objects adapt rectangles in anchors
2369 ScDrawObjData
* pData
= GetObjData(pObj
);
2372 pData
->setShapeRect(GetDocument(), pObj
->GetSnapRect(), pObj
->IsVisible());
2373 ScDrawObjData
* pNoRotatedAnchor
= GetNonRotatedObjData(pObj
, true /*bCreate*/);
2374 pNoRotatedAnchor
->setShapeRect(GetDocument(), pObj
->GetLogicRect(), pObj
->IsVisible());
2378 void ScDrawLayer::MirrorRectRTL( tools::Rectangle
& rRect
)
2380 // mirror and swap left/right
2381 tools::Long nTemp
= rRect
.Left();
2382 rRect
.SetLeft( -rRect
.Right() );
2383 rRect
.SetRight( -nTemp
);
2386 tools::Rectangle
ScDrawLayer::GetCellRect( const ScDocument
& rDoc
, const ScAddress
& rPos
, bool bMergedCell
)
2388 tools::Rectangle aCellRect
;
2389 OSL_ENSURE( rDoc
.ValidColRowTab( rPos
.Col(), rPos
.Row(), rPos
.Tab() ), "ScDrawLayer::GetCellRect - invalid cell address" );
2390 if( rDoc
.ValidColRowTab( rPos
.Col(), rPos
.Row(), rPos
.Tab() ) )
2392 // find top left position of passed cell address
2394 for( SCCOL nCol
= 0; nCol
< rPos
.Col(); ++nCol
)
2395 aTopLeft
.AdjustX(rDoc
.GetColWidth( nCol
, rPos
.Tab() ) );
2396 if( rPos
.Row() > 0 )
2397 aTopLeft
.AdjustY(rDoc
.GetRowHeight( 0, rPos
.Row() - 1, rPos
.Tab() ) );
2399 // find bottom-right position of passed cell address
2400 ScAddress aEndPos
= rPos
;
2403 const ScMergeAttr
* pMerge
= rDoc
.GetAttr( rPos
, ATTR_MERGE
);
2404 if( pMerge
->GetColMerge() > 1 )
2405 aEndPos
.IncCol( pMerge
->GetColMerge() - 1 );
2406 if( pMerge
->GetRowMerge() > 1 )
2407 aEndPos
.IncRow( pMerge
->GetRowMerge() - 1 );
2409 Point aBotRight
= aTopLeft
;
2410 for( SCCOL nCol
= rPos
.Col(); nCol
<= aEndPos
.Col(); ++nCol
)
2411 aBotRight
.AdjustX(rDoc
.GetColWidth( nCol
, rPos
.Tab() ) );
2412 aBotRight
.AdjustY(rDoc
.GetRowHeight( rPos
.Row(), aEndPos
.Row(), rPos
.Tab() ) );
2414 // twips -> 1/100 mm
2415 aTopLeft
= o3tl::convert(aTopLeft
, o3tl::Length::twip
, o3tl::Length::mm100
);
2416 aBotRight
= o3tl::convert(aBotRight
, o3tl::Length::twip
, o3tl::Length::mm100
);
2418 aCellRect
= tools::Rectangle( aTopLeft
, aBotRight
);
2419 if( rDoc
.IsNegativePage( rPos
.Tab() ) )
2420 MirrorRectRTL( aCellRect
);
2425 OUString
ScDrawLayer::GetVisibleName( const SdrObject
* pObj
)
2427 OUString aName
= pObj
->GetName();
2428 if ( pObj
->GetObjIdentifier() == SdrObjKind::OLE2
)
2430 // For OLE, the user defined name (GetName) is used
2431 // if it's not empty (accepting possibly duplicate names),
2432 // otherwise the persist name is used so every object appears
2433 // in the Navigator at all.
2435 if ( aName
.isEmpty() )
2436 aName
= static_cast<const SdrOle2Obj
*>(pObj
)->GetPersistName();
2441 static bool IsNamedObject( const SdrObject
* pObj
, std::u16string_view rName
)
2443 // sal_True if rName is the object's Name or PersistName
2444 // (used to find a named object)
2446 return ( pObj
->GetName() == rName
||
2447 ( pObj
->GetObjIdentifier() == SdrObjKind::OLE2
&&
2448 static_cast<const SdrOle2Obj
*>(pObj
)->GetPersistName() == rName
) );
2451 SdrObject
* ScDrawLayer::GetNamedObject( std::u16string_view rName
, SdrObjKind nId
, SCTAB
& rFoundTab
) const
2453 sal_uInt16 nTabCount
= GetPageCount();
2454 for (sal_uInt16 nTab
=0; nTab
<nTabCount
; nTab
++)
2456 const SdrPage
* pPage
= GetPage(nTab
);
2457 OSL_ENSURE(pPage
,"Page ?");
2460 SdrObjListIter
aIter( pPage
, SdrIterMode::DeepWithGroups
);
2461 while (SdrObject
* pObject
= aIter
.Next())
2463 if ( nId
== SdrObjKind::NONE
|| pObject
->GetObjIdentifier() == nId
)
2464 if ( IsNamedObject( pObject
, rName
) )
2466 rFoundTab
= static_cast<SCTAB
>(nTab
);
2476 OUString
ScDrawLayer::GetNewGraphicName( tools::Long
* pnCounter
) const
2478 OUString aBase
= ScResId(STR_GRAPHICNAME
) + " ";
2481 OUString aGraphicName
;
2483 tools::Long nId
= pnCounter
? *pnCounter
: 0;
2487 aGraphicName
= aBase
+ OUString::number( nId
);
2488 bThere
= ( GetNamedObject( aGraphicName
, SdrObjKind::NONE
, nDummy
) != nullptr );
2494 return aGraphicName
;
2497 void ScDrawLayer::EnsureGraphicNames()
2499 // make sure all graphic objects have names (after Excel import etc.)
2501 sal_uInt16 nTabCount
= GetPageCount();
2502 for (sal_uInt16 nTab
=0; nTab
<nTabCount
; nTab
++)
2504 SdrPage
* pPage
= GetPage(nTab
);
2505 OSL_ENSURE(pPage
,"Page ?");
2508 SdrObjListIter
aIter( pPage
, SdrIterMode::DeepWithGroups
);
2510 /* The index passed to GetNewGraphicName() will be set to
2511 the used index in each call. This prevents the repeated search
2512 for all names from 1 to current index. */
2513 tools::Long nCounter
= 0;
2515 while (SdrObject
* pObject
= aIter
.Next())
2516 if ( pObject
->GetObjIdentifier() == SdrObjKind::Graphic
&& pObject
->GetName().isEmpty())
2517 pObject
->SetName( GetNewGraphicName( &nCounter
) );
2524 SdrObjUserData
* GetFirstUserDataOfType(const SdrObject
*pObj
, sal_uInt16 nId
)
2526 sal_uInt16 nCount
= pObj
? pObj
->GetUserDataCount() : 0;
2527 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
2529 SdrObjUserData
* pData
= pObj
->GetUserData( i
);
2530 if( pData
&& pData
->GetInventor() == SdrInventor::ScOrSwDraw
&& pData
->GetId() == nId
)
2536 void DeleteFirstUserDataOfType(SdrObject
*pObj
, sal_uInt16 nId
)
2538 sal_uInt16 nCount
= pObj
? pObj
->GetUserDataCount() : 0;
2539 for( sal_uInt16 i
= nCount
; i
> 0; i
-- )
2541 SdrObjUserData
* pData
= pObj
->GetUserData( i
-1 );
2542 if( pData
&& pData
->GetInventor() == SdrInventor::ScOrSwDraw
&& pData
->GetId() == nId
)
2543 pObj
->DeleteUserData(i
-1);
2548 void ScDrawLayer::SetNonRotatedAnchor(SdrObject
& rObj
, const ScDrawObjData
& rAnchor
)
2550 ScDrawObjData
* pAnchor
= GetNonRotatedObjData( &rObj
, true );
2551 pAnchor
->maStart
= rAnchor
.maStart
;
2552 pAnchor
->maEnd
= rAnchor
.maEnd
;
2553 pAnchor
->maStartOffset
= rAnchor
.maStartOffset
;
2554 pAnchor
->maEndOffset
= rAnchor
.maEndOffset
;
2555 pAnchor
->mbResizeWithCell
= rAnchor
.mbResizeWithCell
;
2558 void ScDrawLayer::SetCellAnchored( SdrObject
&rObj
, const ScDrawObjData
&rAnchor
)
2560 ScDrawObjData
* pAnchor
= GetObjData( &rObj
, true );
2561 pAnchor
->maStart
= rAnchor
.maStart
;
2562 pAnchor
->maEnd
= rAnchor
.maEnd
;
2563 pAnchor
->maStartOffset
= rAnchor
.maStartOffset
;
2564 pAnchor
->maEndOffset
= rAnchor
.maEndOffset
;
2565 pAnchor
->mbResizeWithCell
= rAnchor
.mbResizeWithCell
;
2568 void ScDrawLayer::SetCellAnchoredFromPosition( SdrObject
&rObj
, const ScDocument
&rDoc
, SCTAB nTab
,
2569 bool bResizeWithCell
)
2571 if (!rObj
.IsVisible())
2573 ScDrawObjData aAnchor
;
2574 // set anchor in terms of the visual ( SnapRect )
2575 // object ( e.g. for when object is rotated )
2576 const tools::Rectangle
aObjRect(rObj
.GetSnapRect());
2577 GetCellAnchorFromPosition(
2583 aAnchor
.mbResizeWithCell
= bResizeWithCell
;
2584 SetCellAnchored( rObj
, aAnchor
);
2586 // absolutely necessary to set flag, ScDrawLayer::RecalcPos expects it.
2587 if ( ScDrawObjData
* pAnchor
= GetObjData( &rObj
) )
2589 pAnchor
->setShapeRect(&rDoc
, rObj
.GetSnapRect());
2592 // - keep also an anchor in terms of the Logic ( untransformed ) object
2593 // because that's what we stored ( and still do ) to xml
2595 // Vertical flipped custom shapes need special treatment, see comment in
2596 // lcl_SetLogicRectFromAnchor
2597 tools::Rectangle aObjRect2
;
2598 if (lcl_NeedsMirrorYCorrection(&rObj
))
2600 const tools::Rectangle
aRect(rObj
.GetSnapRect());
2601 const Point
aLeft(aRect
.Left(), (aRect
.Top() + aRect
.Bottom()) >> 1);
2602 const Point
aRight(aLeft
.X() + 1000, aLeft
.Y());
2603 rObj
.NbcMirror(aLeft
, aRight
);
2604 aObjRect2
= rObj
.GetLogicRect();
2605 rObj
.NbcMirror(aLeft
, aRight
);
2607 else if (rObj
.GetObjIdentifier() == SdrObjKind::Measure
)
2609 // tdf#137576. A SdrMeasureObj might have a wrong logic rect here. TakeUnrotatedSnapRect
2610 // calculates the current unrotated snap rectangle, sets logic rectangle and returns it.
2611 static_cast<SdrMeasureObj
&>(rObj
).TakeUnrotatedSnapRect(aObjRect2
);
2614 aObjRect2
= rObj
.GetLogicRect();
2616 // Values in XML are so as if it is a LTR sheet. The object is shifted to negative page on loading
2617 // so that the snap rectangle appears mirrored. For transformed objects the shifted logic rectangle
2618 // is not the mirrored LTR rectangle. We calculate the mirrored LTR rectangle here.
2619 if (rDoc
.IsNegativePage(nTab
))
2621 const tools::Rectangle
aSnapRect(rObj
.GetSnapRect());
2622 aObjRect2
.Move(Size(-aSnapRect
.Left() - aSnapRect
.Right(), 0));
2623 MirrorRectRTL(aObjRect2
);
2626 ScDrawObjData aNoRotatedAnchor
;
2627 GetCellAnchorFromPosition(
2633 aNoRotatedAnchor
.mbResizeWithCell
= bResizeWithCell
;
2634 SetNonRotatedAnchor( rObj
, aNoRotatedAnchor
);
2635 // And update maShapeRect. It is used in adjustAnchoredPosition() in ScDrawView::Notify().
2636 if (ScDrawObjData
* pAnchor
= GetNonRotatedObjData(&rObj
))
2638 pAnchor
->setShapeRect(&rDoc
, rObj
.GetLogicRect());
2642 void ScDrawLayer::GetCellAnchorFromPosition(
2643 const tools::Rectangle
&rObjRect
,
2644 ScDrawObjData
&rAnchor
,
2645 const ScDocument
&rDoc
,
2649 ScRange aRange
= rDoc
.GetRange( nTab
, rObjRect
, bHiddenAsZero
);
2651 tools::Rectangle aCellRect
;
2653 rAnchor
.maStart
= aRange
.aStart
;
2654 aCellRect
= rDoc
.GetMMRect( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
2655 aRange
.aStart
.Col(), aRange
.aStart
.Row(), aRange
.aStart
.Tab(), bHiddenAsZero
);
2656 rAnchor
.maStartOffset
.setY( rObjRect
.Top()-aCellRect
.Top() );
2657 if (!rDoc
.IsNegativePage(nTab
))
2658 rAnchor
.maStartOffset
.setX( rObjRect
.Left()-aCellRect
.Left() );
2660 rAnchor
.maStartOffset
.setX( aCellRect
.Right()-rObjRect
.Right() );
2662 rAnchor
.maEnd
= aRange
.aEnd
;
2663 aCellRect
= rDoc
.GetMMRect( aRange
.aEnd
.Col(), aRange
.aEnd
.Row(),
2664 aRange
.aEnd
.Col(), aRange
.aEnd
.Row(), aRange
.aEnd
.Tab(), bHiddenAsZero
);
2665 if (!rObjRect
.IsEmpty())
2666 rAnchor
.maEndOffset
.setY( rObjRect
.Bottom()-aCellRect
.Top() );
2667 if (!rDoc
.IsNegativePage(nTab
))
2669 if (!rObjRect
.IsEmpty())
2670 rAnchor
.maEndOffset
.setX( rObjRect
.Right()-aCellRect
.Left() );
2673 rAnchor
.maEndOffset
.setX( aCellRect
.Right()-rObjRect
.Left() );
2676 void ScDrawLayer::UpdateCellAnchorFromPositionEnd( const SdrObject
&rObj
, ScDrawObjData
&rAnchor
, const ScDocument
&rDoc
, SCTAB nTab
, bool bUseLogicRect
)
2678 tools::Rectangle
aObjRect(bUseLogicRect
? rObj
.GetLogicRect() : rObj
.GetSnapRect());
2679 ScRange aRange
= rDoc
.GetRange( nTab
, aObjRect
);
2681 ScDrawObjData
* pAnchor
= &rAnchor
;
2682 pAnchor
->maEnd
= aRange
.aEnd
;
2684 tools::Rectangle aCellRect
= rDoc
.GetMMRect( aRange
.aEnd
.Col(), aRange
.aEnd
.Row(),
2685 aRange
.aEnd
.Col(), aRange
.aEnd
.Row(), aRange
.aEnd
.Tab() );
2686 pAnchor
->maEndOffset
.setY( aObjRect
.Bottom()-aCellRect
.Top() );
2687 if (!rDoc
.IsNegativePage(nTab
))
2688 pAnchor
->maEndOffset
.setX( aObjRect
.Right()-aCellRect
.Left() );
2690 pAnchor
->maEndOffset
.setX( aCellRect
.Right()-aObjRect
.Left() );
2693 bool ScDrawLayer::IsCellAnchored( const SdrObject
& rObj
)
2695 // Cell anchored object always has a user data, to store the anchor cell
2696 // info. If it doesn't then it's page-anchored.
2697 return GetFirstUserDataOfType(&rObj
, SC_UD_OBJDATA
) != nullptr;
2700 bool ScDrawLayer::IsResizeWithCell( const SdrObject
& rObj
)
2702 // Cell anchored object always has a user data, to store the anchor cell
2703 // info. If it doesn't then it's page-anchored.
2704 ScDrawObjData
* pDrawObjData
= GetObjData(const_cast<SdrObject
*>(&rObj
));
2708 return pDrawObjData
->mbResizeWithCell
;
2711 void ScDrawLayer::SetPageAnchored( SdrObject
&rObj
)
2713 DeleteFirstUserDataOfType(&rObj
, SC_UD_OBJDATA
);
2714 DeleteFirstUserDataOfType(&rObj
, SC_UD_OBJDATA
);
2717 ScAnchorType
ScDrawLayer::GetAnchorType( const SdrObject
&rObj
)
2719 //If this object has a cell anchor associated with it
2720 //then it's cell-anchored, otherwise it's page-anchored
2721 const ScDrawObjData
* pObjData
= ScDrawLayer::GetObjData(const_cast<SdrObject
*>(&rObj
));
2723 // When there is no cell anchor, it is page anchored.
2727 // It's cell-anchored, check if the object resizes with the cell
2728 if (pObjData
->mbResizeWithCell
)
2729 return SCA_CELL_RESIZE
;
2734 std::vector
<SdrObject
*>
2735 ScDrawLayer::GetObjectsAnchoredToRows(SCTAB nTab
, SCROW nStartRow
, SCROW nEndRow
)
2737 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
2738 if (!pPage
|| pPage
->GetObjCount() < 1)
2739 return std::vector
<SdrObject
*>();
2741 std::vector
<SdrObject
*> aObjects
;
2742 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
2743 ScRange
aRange( 0, nStartRow
, nTab
, pDoc
->MaxCol(), nEndRow
, nTab
);
2744 while (SdrObject
* pObject
= aIter
.Next())
2746 ScDrawObjData
* pObjData
= GetObjData(pObject
);
2747 if (pObjData
&& aRange
.Contains(pObjData
->maStart
))
2748 aObjects
.push_back(pObject
);
2753 std::map
<SCROW
, std::vector
<SdrObject
*>>
2754 ScDrawLayer::GetObjectsAnchoredToRange(SCTAB nTab
, SCCOL nCol
, SCROW nStartRow
, SCROW nEndRow
)
2756 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
2757 if (!pPage
|| pPage
->GetObjCount() < 1)
2758 return std::map
<SCROW
, std::vector
<SdrObject
*>>();
2760 std::map
<SCROW
, std::vector
<SdrObject
*>> aRowObjects
;
2761 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
2762 ScRange
aRange( nCol
, nStartRow
, nTab
, nCol
, nEndRow
, nTab
);
2763 while (SdrObject
* pObject
= aIter
.Next())
2765 if (!dynamic_cast<SdrCaptionObj
*>(pObject
)) // Caption objects are handled differently
2767 ScDrawObjData
* pObjData
= GetObjData(pObject
);
2768 if (pObjData
&& aRange
.Contains(pObjData
->maStart
))
2769 aRowObjects
[pObjData
->maStart
.Row()].push_back(pObject
);
2775 bool ScDrawLayer::HasObjectsAnchoredInRange(const ScRange
& rRange
)
2777 // This only works for one table at a time
2778 assert(rRange
.aStart
.Tab() == rRange
.aEnd
.Tab());
2780 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(rRange
.aStart
.Tab()));
2781 if (!pPage
|| pPage
->GetObjCount() < 1)
2784 SdrObjListIter
aIter( pPage
, SdrIterMode::Flat
);
2785 while (SdrObject
* pObject
= aIter
.Next())
2787 if (!dynamic_cast<SdrCaptionObj
*>(pObject
)) // Caption objects are handled differently
2789 ScDrawObjData
* pObjData
= GetObjData(pObject
);
2790 if (pObjData
&& rRange
.Contains(pObjData
->maStart
)) // Object is in given range
2797 std::vector
<SdrObject
*> ScDrawLayer::GetObjectsAnchoredToCols(SCTAB nTab
, SCCOL nStartCol
,
2800 SdrPage
* pPage
= GetPage(static_cast<sal_uInt16
>(nTab
));
2801 if (!pPage
|| pPage
->GetObjCount() < 1)
2802 return std::vector
<SdrObject
*>();
2804 std::vector
<SdrObject
*> aObjects
;
2805 SdrObjListIter
aIter(pPage
, SdrIterMode::Flat
);
2806 ScRange
aRange(nStartCol
, 0, nTab
, nEndCol
, pDoc
->MaxRow(), nTab
);
2807 while (SdrObject
* pObject
= aIter
.Next())
2809 ScDrawObjData
* pObjData
= GetObjData(pObject
);
2810 if (pObjData
&& aRange
.Contains(pObjData
->maStart
))
2811 aObjects
.push_back(pObject
);
2816 void ScDrawLayer::MoveObject(SdrObject
* pObject
, const ScAddress
& rNewPosition
)
2819 ScDrawObjData
* pObjData
= GetObjData(pObject
, false);
2822 const ScAddress aOldStart
= pObjData
->maStart
;
2823 const ScAddress aOldEnd
= pObjData
->maEnd
;
2825 // Set start address
2826 pObjData
->maStart
= rNewPosition
;
2829 const SCCOL nObjectColSpan
= aOldEnd
.Col() - aOldStart
.Col();
2830 const SCROW nObjectRowSpan
= aOldEnd
.Row() - aOldStart
.Row();
2831 ScAddress aNewEnd
= rNewPosition
;
2832 aNewEnd
.IncRow(nObjectRowSpan
);
2833 aNewEnd
.IncCol(nObjectColSpan
);
2834 pObjData
->maEnd
= aNewEnd
;
2836 // Update draw object according to new anchor
2837 RecalcPos(pObject
, *pObjData
, false, false);
2840 ScDrawObjData
* ScDrawLayer::GetNonRotatedObjData( SdrObject
* pObj
, bool bCreate
)
2842 sal_uInt16 nCount
= pObj
? pObj
->GetUserDataCount() : 0;
2843 sal_uInt16 nFound
= 0;
2844 for( sal_uInt16 i
= 0; i
< nCount
; i
++ )
2846 SdrObjUserData
* pData
= pObj
->GetUserData( i
);
2847 if( pData
&& pData
->GetInventor() == SdrInventor::ScOrSwDraw
&& pData
->GetId() == SC_UD_OBJDATA
&& ++nFound
== 2 )
2848 return static_cast<ScDrawObjData
*>(pData
);
2850 if( pObj
&& bCreate
)
2852 ScDrawObjData
* pData
= new ScDrawObjData
;
2853 pObj
->AppendUserData(std::unique_ptr
<SdrObjUserData
>(pData
));
2859 ScDrawObjData
* ScDrawLayer::GetObjData( SdrObject
* pObj
, bool bCreate
)
2861 if (SdrObjUserData
*pData
= GetFirstUserDataOfType(pObj
, SC_UD_OBJDATA
))
2862 return static_cast<ScDrawObjData
*>(pData
);
2864 if( pObj
&& bCreate
)
2866 ScDrawObjData
* pData
= new ScDrawObjData
;
2867 pObj
->AppendUserData(std::unique_ptr
<SdrObjUserData
>(pData
));
2873 ScDrawObjData
* ScDrawLayer::GetObjDataTab( SdrObject
* pObj
, SCTAB nTab
)
2875 ScDrawObjData
* pData
= GetObjData( pObj
);
2878 if ( pData
->maStart
.IsValid() )
2879 pData
->maStart
.SetTab( nTab
);
2880 if ( pData
->maEnd
.IsValid() )
2881 pData
->maEnd
.SetTab( nTab
);
2886 bool ScDrawLayer::IsNoteCaption(const ScDrawObjData
* pData
)
2888 return pData
&& pData
->meType
== ScDrawObjData::CellNote
;
2891 ScDrawObjData
* ScDrawLayer::GetNoteCaptionData( SdrObject
* pObj
, SCTAB nTab
)
2893 ScDrawObjData
* pData
= GetObjDataTab(pObj
, nTab
);
2894 return IsNoteCaption(pData
) ? pData
: nullptr;
2897 ScMacroInfo
* ScDrawLayer::GetMacroInfo( SdrObject
* pObj
, bool bCreate
)
2899 if (SdrObjUserData
*pData
= GetFirstUserDataOfType(pObj
, SC_UD_MACRODATA
))
2900 return static_cast<ScMacroInfo
*>(pData
);
2904 ScMacroInfo
* pData
= new ScMacroInfo
;
2905 pObj
->AppendUserData(std::unique_ptr
<SdrObjUserData
>(pData
));
2911 void ScDrawLayer::SetGlobalDrawPersist(SfxObjectShell
* pPersist
)
2913 OSL_ENSURE(!pGlobalDrawPersist
,"Multiple SetGlobalDrawPersist");
2914 pGlobalDrawPersist
= pPersist
;
2917 void ScDrawLayer::SetChanged( bool bFlg
/* = true */ )
2920 pDoc
->SetChartListenerCollectionNeedsUpdate( true );
2921 FmFormModel::SetChanged( bFlg
);
2924 css::uno::Reference
< css::frame::XModel
> ScDrawLayer::createUnoModel()
2926 css::uno::Reference
< css::frame::XModel
> xRet
;
2927 if( pDoc
&& pDoc
->GetDocumentShell() )
2928 xRet
= pDoc
->GetDocumentShell()->GetModel();
2933 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */